R DATA VISUALIZATION
Why Data Visualization?
Raw data point doesn’t provide much insight to kick off data
analysis.
Data Visualization is brilliant in * exploring the pattern of data
briefly at the early stage * in the final conclusion, enhance the story
telling of data analysis (inforgraphics play a huge role for this
purpose)
Reminder on Workflow Again
Like the picture above shows, in the
stage of Understand (exploring the data sets),
Transform, Visualize and Model are used in an iterative manner so as to
get the best early understaning about the data sets.
Built-in Plot Functions
The advantage of using built-in plotting utilities is they are easy.
It let you quickly visualize the data pattern while you are trying to
gain a brief insight at the early stage of your workflow.

Grammar of Graphics: ggplot2
If these built-in plotting tools are not enough for you, Go to
ggplot2. It is the most popular data visualization for
R.
ggplot2 is an open-source data visualization package for R. A data
visualization which breaks up graphs into semantic components such as
scales and layers. Since 2005,
ggplot2 has grown in use to become one of the most popular R
packages.
INSTALL / LOAD PACKAGES
if (!require("pacman")) install.packages("pacman") # check if pacman already installed. If not, install it.
pacman::p_load(
pacman, # package manager
datasets, # built-in data sets
rio, # r input / output
magrittr, # for piping commands
tidyverse,
modelr
) # Load required packages. If they are NOT already downloaded, download them automatically.
BASIC GRAMMAR
ggplot2 is based on the grammar of graphics, the idea that you can
build every graph from the same components below:
a data set + a coordinate system +
and geoms—visual marks that represent data points
Use Built-in Datasets
Let’s use the built-in cars data set for a simple ggplot

geom_point() function
It’s easy to add geometry layer to the base co-ordinate
Let’s ADD a layer of data points using geom_point()
function.
And Yes, you can ADD a layer by using the + operator We use
geom_point() function that required x and y value for
each point. In 2D co-ordinate, a point is described by its x and y
value.
We need to provide a mapping that specifies the data columns’ name to
map to a point’s the x and y value
That mapping is defined by an aesthetics function
aes()
Scatter plot is useful to explore the relation of two variables.

Use geom_line() to replace geom_point()
geom_point() and geom_line() require very similar parameters.
geom_line() is simply an enhanced visualization that automatically
connect all the points

Use geom_smoth() to project a smooth line
again geom_smooth() and geom_point() require very similar
parameters.
geom_smooth() smooths out the line progression
cars %>% ggplot() +
geom_smooth(mapping = aes(x=speed, y=dist))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Geometric Objects Comparison
a geom is the geometrical object that a plot use to represent data.
We used points, lines and smooth above on the same data set and they
provide very different message.
Adding Multiple Layers
# just change geom_point to geom_line without change anything else
cars %>% ggplot() +
geom_point(mapping = aes(x=speed, y=dist)) +
geom_smooth(mapping = aes(x=speed, y=dist))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Adding Aesthetics to your Plots
Un-comment the extra parameters to add more aesthetics to your
plot
cars %>% ggplot() +
geom_point(mapping = aes(x=speed, y=dist),
color = "orange", # the color of data points
# size = 3, # the size of data point
# alpha = 0.5, # the transparency of data points, min=0, max=1
# shape = 0, # the shape of data point
)

PLOT WITH OUR OWN DATA
Allowance Data Set in Simple Scatterplot

Continuous Values vs. Discrete Values
Continuous values refer to numbers value that has wide range. E.g.
salary, height Discrete values refer to a limited number of valid
values. It can be string. It can be a few distinct numbers.
When you produce plots, pay attention to what type of value are
required by the geoms.
In many cases, you will need to convert the data first.
mutate() function are quite often used for that.
example:
allowance = allowance %>%
mutate(Assessment_Year = as.numeric(substr(Assessment_Year, 1 ,4)))
Simple Line Plot

Adding Multiple Layers of Geometry
allowance %>% ggplot() +
geom_line(mapping = aes(x=Assessment_Year, y=Basic, group=1, color="Orange")) +
geom_point(mapping = aes(x=Assessment_Year, y=Basic, group=1, color="Orange"))

# As both geom use the same data mapping, the above statements can be simplified as
allowance %>% ggplot(aes(x=Assessment_Year, y=Basic, group=1, color="Orange")) +
geom_line() +
geom_point(size=5)

Use geom_smooth() to smooth out the line
allowance %>% ggplot(aes(x=Assessment_Year, y=Basic, group=1, color="Orange")) +
geom_smooth() +
geom_point(size=5)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Save a Plot: ggsave()
ggsave("./output/my_first_plot.png") # default image size
Saving 7 x 7 in image
Bar Chart with geom_col()

geom_bar()
geom_bar() is used for counting the frequency of each occurrence of
observed value. It’s usually for counting a limit set of value

CHALLENGE: line plot for hibor_fixing_1m
library(jsonlite) # load package
hkma.interbank.url = "https://api.hkma.gov.hk/public/market-data-and-statistics/daily-monetary-statistics/daily-figures-interbank-liquidity"
interbank.liquidity = fromJSON(hkma.interbank.url)
# the above retrieval will take a while. The server response is slow.
summary(interbank.liquidity)
Length Class Mode
header 3 -none- list
result 2 -none- list
str(interbank.liquidity)
List of 2
$ header:List of 3
..$ success : logi TRUE
..$ err_code: chr "0000"
..$ err_msg : chr "No error found"
$ result:List of 2
..$ datasize: int 100
..$ records :'data.frame': 100 obs. of 44 variables:
.. ..$ end_of_date : chr [1:100] "2022-03-30" "2022-03-29" "2022-03-28" "2022-03-25" ...
.. ..$ cu_weakside : num [1:100] 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 ...
.. ..$ cu_strongside : num [1:100] 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 ...
.. ..$ disc_win_base_rate : num [1:100] 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 ...
.. ..$ hibor_overnight : num [1:100] 0.03 0.02 0.02 0.02 0.02 0.02 0.02 0.01 0.03 0.02 ...
.. ..$ hibor_fixing_1m : num [1:100] 0.313 0.312 0.313 0.323 0.319 ...
.. ..$ twi : num [1:100] 95.3 95.7 95.9 95.6 95.7 95.7 95.6 95.5 95.4 95.4 ...
.. ..$ opening_balance : int [1:100] 337534 337534 337534 337534 337534 337536 337553 337538 337538 337538 ...
.. ..$ closing_balance : int [1:100] 337551 337534 337534 337534 337534 337534 337536 337553 337538 337538 ...
.. ..$ market_activities : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ interest_payment : chr [1:100] "+16" "+0" "+0" "+0" ...
.. ..$ discount_window_reversal : chr [1:100] "-0" "-0" "-0" "-0" ...
.. ..$ discount_window_activities : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ intraday_movements_of_aggregate_balance_at_0930: int [1:100] 363880 350696 357104 354934 363276 355552 353301 352138 351573 353926 ...
.. ..$ intraday_movements_of_aggregate_balance_at_1000: int [1:100] 366723 358279 358428 358128 363476 361781 356632 356870 353300 355233 ...
.. ..$ intraday_movements_of_aggregate_balance_at_1100: int [1:100] 383598 376635 375870 374775 374465 377616 374645 370373 371865 373477 ...
.. ..$ intraday_movements_of_aggregate_balance_at_1200: int [1:100] 331056 383952 385132 383777 383416 323375 375169 381656 378485 395569 ...
.. ..$ intraday_movements_of_aggregate_balance_at_1500: int [1:100] 334817 391180 389282 382202 390063 335473 391970 403349 403099 398017 ...
.. ..$ intraday_movements_of_aggregate_balance_at_1600: int [1:100] 337197 392468 391754 383194 391833 337396 392436 403610 409198 401337 ...
.. ..$ forex_trans_t1 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ other_market_activities_t1 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ reversal_of_discount_window_t1 : chr [1:100] "-0" "-0" "-0" "-0" ...
.. ..$ interest_payment_issuance_efbn_t1 : chr [1:100] "+0" "+16" "+0" "+0" ...
.. ..$ forecast_aggregate_bal_t1 : int [1:100] 337551 337551 337534 337534 337534 337534 337534 337536 337536 337538 ...
.. ..$ forex_trans_t2 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ other_market_activities_t2 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ reversal_of_discount_window_t2 : chr [1:100] "-0" "-0" "-0" "-0" ...
.. ..$ interest_payment_issuance_efbn_t2 : chr [1:100] "+0" "+0" "+7" "+0" ...
.. ..$ forecast_aggregate_bal_t2 : int [1:100] 337551 337551 337541 337534 337534 337534 337534 337529 337536 337536 ...
.. ..$ forex_trans_t3 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ other_market_activities_t3 : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ reversal_of_discount_window_t3 : chr [1:100] "-0" "-0" "-0" "-0" ...
.. ..$ interest_payment_issuance_efbn_t3 : chr [1:100] "+0" "+0" "+0" "+7" ...
.. ..$ forecast_aggregate_bal_t3 : int [1:100] 337551 337551 337541 337541 337534 337534 337534 337529 337529 337536 ...
.. ..$ forex_trans_t4 : chr [1:100] NA NA NA NA ...
.. ..$ other_market_activities_t4 : chr [1:100] NA NA NA NA ...
.. ..$ reversal_of_discount_window_t4 : chr [1:100] NA NA NA NA ...
.. ..$ interest_payment_issuance_efbn_t4 : chr [1:100] NA NA NA NA ...
.. ..$ forecast_aggregate_bal_t4 : int [1:100] NA NA NA NA NA NA NA NA NA NA ...
.. ..$ forex_trans_u : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ other_market_activities_u : chr [1:100] "+0" "+0" "+0" "+0" ...
.. ..$ reversal_of_discount_window_u : chr [1:100] "-0" "-0" "-0" "-0" ...
.. ..$ interest_payment_issuance_efbn_u : chr [1:100] "-72" "-72" "-62" "-62" ...
.. ..$ forecast_aggregate_bal_u : int [1:100] 337479 337479 337479 337479 337479 337479 337479 337479 337479 337479 ...
interbank.liquidity$result
$datasize
[1] 100
$records
str(interbank.liquidity$result)
List of 2
$ datasize: int 100
$ records :'data.frame': 100 obs. of 44 variables:
..$ end_of_date : chr [1:100] "2022-03-30" "2022-03-29" "2022-03-28" "2022-03-25" ...
..$ cu_weakside : num [1:100] 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 7.85 ...
..$ cu_strongside : num [1:100] 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 7.75 ...
..$ disc_win_base_rate : num [1:100] 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 ...
..$ hibor_overnight : num [1:100] 0.03 0.02 0.02 0.02 0.02 0.02 0.02 0.01 0.03 0.02 ...
..$ hibor_fixing_1m : num [1:100] 0.313 0.312 0.313 0.323 0.319 ...
..$ twi : num [1:100] 95.3 95.7 95.9 95.6 95.7 95.7 95.6 95.5 95.4 95.4 ...
..$ opening_balance : int [1:100] 337534 337534 337534 337534 337534 337536 337553 337538 337538 337538 ...
..$ closing_balance : int [1:100] 337551 337534 337534 337534 337534 337534 337536 337553 337538 337538 ...
..$ market_activities : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ interest_payment : chr [1:100] "+16" "+0" "+0" "+0" ...
..$ discount_window_reversal : chr [1:100] "-0" "-0" "-0" "-0" ...
..$ discount_window_activities : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ intraday_movements_of_aggregate_balance_at_0930: int [1:100] 363880 350696 357104 354934 363276 355552 353301 352138 351573 353926 ...
..$ intraday_movements_of_aggregate_balance_at_1000: int [1:100] 366723 358279 358428 358128 363476 361781 356632 356870 353300 355233 ...
..$ intraday_movements_of_aggregate_balance_at_1100: int [1:100] 383598 376635 375870 374775 374465 377616 374645 370373 371865 373477 ...
..$ intraday_movements_of_aggregate_balance_at_1200: int [1:100] 331056 383952 385132 383777 383416 323375 375169 381656 378485 395569 ...
..$ intraday_movements_of_aggregate_balance_at_1500: int [1:100] 334817 391180 389282 382202 390063 335473 391970 403349 403099 398017 ...
..$ intraday_movements_of_aggregate_balance_at_1600: int [1:100] 337197 392468 391754 383194 391833 337396 392436 403610 409198 401337 ...
..$ forex_trans_t1 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ other_market_activities_t1 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ reversal_of_discount_window_t1 : chr [1:100] "-0" "-0" "-0" "-0" ...
..$ interest_payment_issuance_efbn_t1 : chr [1:100] "+0" "+16" "+0" "+0" ...
..$ forecast_aggregate_bal_t1 : int [1:100] 337551 337551 337534 337534 337534 337534 337534 337536 337536 337538 ...
..$ forex_trans_t2 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ other_market_activities_t2 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ reversal_of_discount_window_t2 : chr [1:100] "-0" "-0" "-0" "-0" ...
..$ interest_payment_issuance_efbn_t2 : chr [1:100] "+0" "+0" "+7" "+0" ...
..$ forecast_aggregate_bal_t2 : int [1:100] 337551 337551 337541 337534 337534 337534 337534 337529 337536 337536 ...
..$ forex_trans_t3 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ other_market_activities_t3 : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ reversal_of_discount_window_t3 : chr [1:100] "-0" "-0" "-0" "-0" ...
..$ interest_payment_issuance_efbn_t3 : chr [1:100] "+0" "+0" "+0" "+7" ...
..$ forecast_aggregate_bal_t3 : int [1:100] 337551 337551 337541 337541 337534 337534 337534 337529 337529 337536 ...
..$ forex_trans_t4 : chr [1:100] NA NA NA NA ...
..$ other_market_activities_t4 : chr [1:100] NA NA NA NA ...
..$ reversal_of_discount_window_t4 : chr [1:100] NA NA NA NA ...
..$ interest_payment_issuance_efbn_t4 : chr [1:100] NA NA NA NA ...
..$ forecast_aggregate_bal_t4 : int [1:100] NA NA NA NA NA NA NA NA NA NA ...
..$ forex_trans_u : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ other_market_activities_u : chr [1:100] "+0" "+0" "+0" "+0" ...
..$ reversal_of_discount_window_u : chr [1:100] "-0" "-0" "-0" "-0" ...
..$ interest_payment_issuance_efbn_u : chr [1:100] "-72" "-72" "-62" "-62" ...
..$ forecast_aggregate_bal_u : int [1:100] 337479 337479 337479 337479 337479 337479 337479 337479 337479 337479 ...
interbank.records = interbank.liquidity$result$records %>% as_tibble()
interbank.records
interbank.records %>%
ggplot() +
geom_line(
mapping=aes(x=end_of_date, y=hibor_fixing_1m, group=1),
color="orange"
)

GROUPING AND AGGREGATION
Using group_by() and summarise()
graduates %>% group_by(AcademicYear, LevelOfStudy) %>%
summarise(TotalHeadcount = sum(Headcount)) %>%
ggplot(
aes(x=AcademicYear,
y=TotalHeadcount,
group=LevelOfStudy,
color=LevelOfStudy
)
) +
geom_line() +
geom_point()
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups`
argument.

Use of filter()
Use filter() to keep only “Taught Postgraduate” Records
This plot is not very useful without previously applying filter() and
group_by() and summarise()

filter() + group_by() + summarise()
Use filter() to extract required rows Use group_by() and summarise()
to group and aggreate total headcout for both male and female
graduates %>%
filter(LevelOfStudy=="Taught Postgraduate") %>%
group_by(AcademicYear, ProgrammeCategory) %>%
summarise(TotalHeadcount = sum(Headcount)) %>%
ggplot() +
geom_line(mapping=aes(x=AcademicYear,y=TotalHeadcount, group=ProgrammeCategory, color=ProgrammeCategory))
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups`
argument.

graduates %>%
filter(LevelOfStudy=="Undergraduate") %>%
group_by(AcademicYear, ProgrammeCategory) %>%
summarise(TotalHeadcount = sum(Headcount)) %>%
ggplot() +
geom_line(mapping=aes(x=AcademicYear,y=TotalHeadcount, group=ProgrammeCategory, color=ProgrammeCategory))
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups`
argument.

NA
geom_col() function

More Aggregation Functions
Center: mean(), median() Spread: sd(), IQR(), mad() Range: min(),
max(), quantile() Position: first(), last(), nth(), Count: n(),
n_distinct() Logical: any(), all()
More information at summarise()
function
geom_bar() function
bar chart give the counting frequency (number of record in the data
set)

box plot
The boxplot compactly displays the distribution of a continuous
variable. It visualises five summary statistics (the median, two hinges
and two whiskers), and all “outlying” points individually.


facet_wrap()
facets are useful categorical variables. It split your plot into
subplot (a.k.a facets) that each display one subset of the data.
facet_wrap() lets you work with ONE extra variable (besides x and
y).
Each categorical value will used to produce to a sub plot. We use
LevelOfStudy here. Since there are FOUR distinct values, you will see
FOUR sub-plots
graduates %>% ggplot() +
geom_point(mapping = aes(x=AcademicYear, y=Headcount)) +
facet_wrap(~ LevelOfStudy) # You will see FOUR sub plots as there are FOUR distinct values for this variable.

facet_grid()
facet_grid() lets you work with TWO extra variables (besides x and
y).
Each combination of these two varaibles’ value are used to produce to a
sub plot.
You should SEE 28 sub plots as there are FOUR distinct values for
LevelOfStudy and 7 distinct values for ProgrammeCategory.
graduates %>% ggplot() +
geom_point(mapping = aes(x=AcademicYear, y=Headcount)) +
facet_grid(LevelOfStudy ~ ProgrammeCategory)

MAKE IT PRETTY
Use of title, label, background color and themes
# in this example we save the plot to a variable name 'level.bar.plot' so that we can use it again and again.
level.bar.plot = graduates %>%
filter(ProgrammeCategory=="Engineering and Technology") %>%
ggplot() +
geom_col(mapping=aes(x=AcademicYear, y=Headcount, fill=LevelOfStudy))
# To show the plot, just use print() function with the previous saved plot variable as parameter.
print(level.bar.plot)

Plot Background
element_rect() is a function to generated rectangle
geometry element. You have to specify the fill
parameter by color name or hex code code by string
Plot Background refers to the big area of everything relevant to the
plot.

Panel Background
Panel background refers to the inner area of plot. Area for showing
header, axis lable and legend are NOT included.


level.bar.plot # default style
level.bar.plot # default style
level.bar.plot +
theme(panel.background = element_rect(fill="orange")) # styling the panel background

Remove Plot and Panel Background
In visual design, color is very powerful tool to guide users’
attention. But you have to use them carefully.
Too many colors will usually do the opposite - confuse the audience.
Minimal design is the recent trend. Expecially true when many are using
small device like mobile phone for day-to-day communication.
In this example, we are removing both plot and panel background to
achieve a clean design. After all, background is the main dish. Very
often background color causes distraction to graph.

Change Label for x/y Axis
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") # Label for X axis

Ratate the Labe Text
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(axis.text.x = element_text(angle = 45))

Change Fill Colors
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"))

Styling The Legends
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
)

NA
Add Title and Subtitle
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
) + # move legend position to top and label position to bottom
ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019")

Add Annotations Texts
Add extra texts/shape to enhance your visualization
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
) + # move legend position to top and label position to bottom
ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019") +
annotate("text", label="Record\nHigh", x="2017/18", y=5300) # you can change value of x and y to set the text position

Adding Reference Lines

Using Themes
level.bar.plot # default style

level.bar.plot +
theme_bw() # black and white theme

level.bar.plot +
theme_minimal() # black and white theme

level.bar.plot +
theme_dark() # black and white theme

More 3rd-party Themes
Install ggthemes package to unlock wider selections
of themes.
if (!require("pacman")) install.packages("pacman") # check if pacman already installed. If not, install it.
pacman::p_load(ggthemes)
level.bar.plot # default style

level.bar.plot +
theme_excel() + # Excel Theme
ggtitle("Excel Theme")

level.bar.plot +
theme_wsj() + # Wall Street Journal Theme
ggtitle("Wall Street Journal Theme")

level.bar.plot +
theme_economist() + # Economist Theme
ggtitle("Economist Theme")

level.bar.plot +
theme_fivethirtyeight() + # Five Thirty Eight
ggtitle("Five Thirty Eight Theme")

MODELS
Data Science is combination of efforts and results of programming,
mathematics and domain expertise. Among all, mathematics is the
foundation of models. With models, data scientists make predictions;
discover hidden patterns; and conclude insights.
Modeling is usually an iterative process among data transformation,
data visualization, exploring with models and fitting.
What exactly is a Model?
Human are good in drawing conclusions and providing insight while are
NOT good in directly facing large number of data attributes and huge
volume of raw data.
A model is mathematics expression that provides a simple
low-dimensional summary of a data set so that we can
draw conclusion and even providing insights. Models only provide
approximation (NOT the exact truth).
Basic Concepts of Model
Let’s do some simple R coding to uncover the basic concept of
model
if (!require("pacman")) install.packages("pacman") # install pacman
pacman::p_load(pacman, tidyverse, modelr, magrittr) # install (or load) required packages
Let’s use a simple built-in data set sim1 for
exploring. In this simulation data you can strongly see the pattern with
the help of simple scatter-plot.
Generating a Random Linear Model
Linear line and quadratic curve are widely used to explore the
relation of two variables. Let’s take linear model as simple example to
grab the essence of model.
A linear model is described as y = a1 + x * a2 x and y are the
variable from dataset a1 and a2 are parameters that can vary to capture
different patterns.
Let’s generate a random value of a1 as intercept and
a2 as slope. Here, we use runinf() to
generate a random uniform distributed number
Note: You might have to repeatively run a few times
before you can see the visualized random model represented by a orange
straight line.

Generating 250 Random Models as Candidate Models
The number of potential models are unlimited. Let’s try to generate
250 random ones as candidate models.
Among these 250 models, some are very bad that you can judge even by
glancing. Some are not bad but we don’t know which one is the best among
them.
models = tibble(
a1 = runif(250, -20, 40), # 250 random intercept values between -20 to 40
a2 = runif(250, -5, 5) # 250 random slop values between -5 to 5
)
ggplot(sim1, aes(x,y)) +
geom_point() +
geom_abline(aes(intercept = a1, slope = a2), data=models, alpha=0.2)

Selecting the Most Fitting Ten Models

Using lm() function
If the previous R codes on choosing best 10 among 250 random models
are too much digest. It’s fine. It’s just for you to feel the process
and essence of models and fitting models.
In fact, R makes linear model fitting extremely easy by just one
single line of function calling to the lm() function (a
built-in linear model fitting function)
lm() finds the closest model in a single step, using
a sophisticated algorithm that involves geometry, calculus, and linear
algebra.
lm() has a special way to specify the model family:
formulas. Formulas look like y ~ x, which lm() will
translate to a function like y = a1 + a2 * x
sim1_auto_model = sim1 %>% lm(y ~ x, .) # finding the optimized linear model
sim1_auto_model = sim1 %>% lm(y ~ x, .) # finding the optimized linear model
print(sim1_auto_model) # print out the auto generated linear model
Call:
lm(formula = y ~ x, data = .)
Coefficients:
(Intercept) x
4.221 2.052
Making Prediction
new.data = data.frame(x = c(1,2,3,4,5,6,7,8,9,10))
predict(sim1_auto_model, new.data)
1 2 3 4 5 6 7 8
6.272355 8.323888 10.375421 12.426954 14.478487 16.530020 18.581553 20.633087
9 10
22.684620 24.736153
Using lm() function with categorical data
Using lm() function on a categorical variable will use mean value for
each category for prediction.

Multiple Regression
Multiple regression is an extension of linear regression into
relationship between more than two variables.
predict(m.r.model, new.data.2)
1
22.65987
Logistic Regression
summary(bi.model)
Call:
glm(formula = am ~ cyl + hp + wt, family = binomial, data = .)
Deviance Residuals:
Min 1Q Median 3Q Max
-2.17272 -0.14907 -0.01464 0.14116 1.27641
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 19.70288 8.11637 2.428 0.0152 *
cyl 0.48760 1.07162 0.455 0.6491
hp 0.03259 0.01886 1.728 0.0840 .
wt -9.14947 4.15332 -2.203 0.0276 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 43.2297 on 31 degrees of freedom
Residual deviance: 9.8415 on 28 degrees of freedom
AIC: 17.841
Number of Fisher Scoring iterations: 8

LS0tCnRpdGxlOiAiUiBJbnRlcm1lZGlhdGUgLSBEYXkgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBSIERBVEEgVklTVUFMSVpBVElPTgoKIyMgV2h5IERhdGEgVmlzdWFsaXphdGlvbj8KUmF3IGRhdGEgcG9pbnQgZG9lc24ndCBwcm92aWRlIG11Y2ggaW5zaWdodCB0byBraWNrIG9mZiBkYXRhIGFuYWx5c2lzLlwKCkRhdGEgVmlzdWFsaXphdGlvbiBpcyBicmlsbGlhbnQgaW4KKiBleHBsb3JpbmcgdGhlIHBhdHRlcm4gb2YgZGF0YSBicmllZmx5IGF0IHRoZSBlYXJseSBzdGFnZQoqIGluIHRoZSBmaW5hbCBjb25jbHVzaW9uLCBlbmhhbmNlIHRoZSBzdG9yeSB0ZWxsaW5nIG9mIGRhdGEgYW5hbHlzaXMgKGluZm9yZ3JhcGhpY3MgcGxheSBhIGh1Z2Ugcm9sZSBmb3IgdGhpcyBwdXJwb3NlKQoKIyMgUmVtaW5kZXIgb24gV29ya2Zsb3cgQWdhaW4KCiFbUiBEYXRhIFNjaWVuY2UgV29ya2Zsb3ddKGh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvNTcxYjA1Njc1N2Q2OGU2ZGY4MWEzZTM4NTNmNTRkM2M3NmFkNmVmYy8zMmQzNy9kaWFncmFtcy9kYXRhLXNjaWVuY2UucG5nKQpMaWtlIHRoZSBwaWN0dXJlIGFib3ZlIHNob3dzLCBpbiB0aGUgc3RhZ2Ugb2YgKipVbmRlcnN0YW5kKiogKGV4cGxvcmluZyB0aGUgZGF0YSBzZXRzKSwgVHJhbnNmb3JtLCBWaXN1YWxpemUgYW5kIE1vZGVsIGFyZSB1c2VkIGluIGFuIGl0ZXJhdGl2ZSBtYW5uZXIgc28gYXMgdG8gZ2V0IHRoZSBiZXN0IGVhcmx5IHVuZGVyc3RhbmluZyBhYm91dCB0aGUgZGF0YSBzZXRzLgoKCiMjIEJ1aWx0LWluIFBsb3QgRnVuY3Rpb25zClRoZSBhZHZhbnRhZ2Ugb2YgdXNpbmcgYnVpbHQtaW4gcGxvdHRpbmcgdXRpbGl0aWVzIGlzIHRoZXkgYXJlIGVhc3kuCkl0IGxldCB5b3UgcXVpY2tseSB2aXN1YWxpemUgdGhlIGRhdGEgcGF0dGVybiB3aGlsZSB5b3UgYXJlIHRyeWluZyB0byBnYWluIGEgYnJpZWYgaW5zaWdodCBhdCB0aGUgZWFybHkgc3RhZ2Ugb2YgeW91ciB3b3JrZmxvdy4KYGBge3J9CnBsb3QoaXJpcykKYGBgCgojIyBSZWZsYXNoIG9uIEJ1aWx0LWluIFBsb3QgVG9vbHMKRm9yIGJ1aWx0LWluIFIgZGF0YSB2aXN1YWxpemF0aW9uLCBnbyB0byB0aGUgKipSIFByb2dyYW1taW5nIEludHJvKiogcHJvamVjdCBvbiBHaXRodWIgdG8gcmVmbGFzaCB5b3VyIG1lbW9yeQooUiBJbnRybyBTb3VyY2UgQ29kZXMpW2h0dHBzOi8vZ2l0aHViLmNvbS9uZ3Nhbmx1ay9SLUludHJvXQoKCiMjIEdyYW1tYXIgb2YgR3JhcGhpY3M6IGdncGxvdDIKCklmIHRoZXNlIGJ1aWx0LWluIHBsb3R0aW5nIHRvb2xzIGFyZSBub3QgZW5vdWdoIGZvciB5b3UsXCAKR28gdG8gKipnZ3Bsb3QyKiouIEl0ICBpcyB0aGUgbW9zdCBwb3B1bGFyIGRhdGEgdmlzdWFsaXphdGlvbiBmb3IgUi4KCmdncGxvdDIgaXMgYW4gb3Blbi1zb3VyY2UgZGF0YSB2aXN1YWxpemF0aW9uIHBhY2thZ2UgZm9yIFIuIApBIGRhdGEgdmlzdWFsaXphdGlvbiB3aGljaCBicmVha3MgdXAgZ3JhcGhzIGludG8gc2VtYW50aWMgY29tcG9uZW50cyBzdWNoIGFzICoqc2NhbGVzKiogYW5kICoqbGF5ZXJzKiouXCAKU2luY2UgMjAwNSwgZ2dwbG90MiBoYXMgZ3Jvd24gaW4gdXNlIHRvIGJlY29tZSBvbmUgb2YgdGhlIG1vc3QgcG9wdWxhciBSIHBhY2thZ2VzLgoKCiMjIGdncGxvdDIgQ2hlYXQgU2hlZXQKCltnZ3Bsb3QgMiBjaGVhdCBzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvYmxvYi9tYWluL2RhdGEtdmlzdWFsaXphdGlvbi0yLjEucGRmKQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIElOU1RBTEwgLyBMT0FEIFBBQ0tBR0VTCmBgYHtyfQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKSAjIGNoZWNrIGlmIHBhY21hbiBhbHJlYWR5IGluc3RhbGxlZC4gSWYgbm90LCBpbnN0YWxsIGl0LgpwYWNtYW46OnBfbG9hZCgKICBwYWNtYW4sICMgcGFja2FnZSBtYW5hZ2VyCiAgZGF0YXNldHMsICMgYnVpbHQtaW4gZGF0YSBzZXRzCiAgcmlvLCAjIHIgaW5wdXQgLyBvdXRwdXQKICBtYWdyaXR0ciwgIyBmb3IgcGlwaW5nIGNvbW1hbmRzCiAgdGlkeXZlcnNlLCAKICBtb2RlbHIgIyBtYXRoZW1hdGljcyBtb2RlbAopICMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcy4gIElmIHRoZXkgYXJlIE5PVCBhbHJlYWR5IGRvd25sb2FkZWQsIGRvd25sb2FkIHRoZW0gYXV0b21hdGljYWxseS4KYGBgCgojIEJBU0lDIEdSQU1NQVIKZ2dwbG90MiBpcyBiYXNlZCBvbiB0aGUgZ3JhbW1hciBvZiBncmFwaGljcywgdGhlIGlkZWEgdGhhdCB5b3UgY2FuIGJ1aWxkIGV2ZXJ5IGdyYXBoIGZyb20gdGhlIHNhbWUgY29tcG9uZW50cyBiZWxvdzogCgoqKmEgZGF0YSBzZXQqKiAKKyAKKiphIGNvb3JkaW5hdGUgc3lzdGVtKiogCisgCioqYW5kIGdlb21z4oCUdmlzdWFsIG1hcmtzIHRoYXQgcmVwcmVzZW50IGRhdGEgcG9pbnRzKioKCiFbR3JhbW1hciBvZiBHcmFwaGljc10oaHR0cHM6Ly9qdWxlczMyLmdpdGh1Yi5pby9yLWZvci1leGNlbC11c2Vycy9pbWcvcnN0dWRpby1jaGVhdHNoZWV0LWdncGxvdC5wbmcpCgoKIyMgVXNlIEJ1aWx0LWluIERhdGFzZXRzCkxldCdzIHVzZSB0aGUgYnVpbHQtaW4gY2FycyBkYXRhIHNldCBmb3IgYSBzaW1wbGUgZ2dwbG90CgpgYGB7cn0KcHJpbnQoY2FycykKYGBgCgpgYGB7ciBHZW5lcmF0aW5nIEVtcHR5IFBsb3R9CmNhcnMgJT4lIGdncGxvdCgpICMgVGhpcyBvbmx5IHNwZWNpZmllcyBhIGRhdGEgc2V0IGFuZCBhIGNvb3JkaW5hdGUgc3lzdGVtIGFuZCB0aGVyZWZvcmUgYW4gZW1wdHkgcGxvdApgYGAKCiMjIGdlb21fcG9pbnQoKSBmdW5jdGlvbgpJdCdzIGVhc3kgdG8gYWRkIGdlb21ldHJ5IGxheWVyIHRvIHRoZSBiYXNlIGNvLW9yZGluYXRlXApMZXQncyBBREQgYSBsYXllciBvZiBkYXRhIHBvaW50cyB1c2luZyAqKmdlb21fcG9pbnQoKSoqIGZ1bmN0aW9uLiBcCgpBbmQgWWVzLCB5b3UgY2FuIEFERCBhIGxheWVyIGJ5IHVzaW5nIHRoZSArIG9wZXJhdG9yCldlIHVzZSAqKmdlb21fcG9pbnQoKSoqIGZ1bmN0aW9uIHRoYXQgcmVxdWlyZWQgeCBhbmQgeSB2YWx1ZSBmb3IgZWFjaCBwb2ludC4KSW4gMkQgY28tb3JkaW5hdGUsIGEgcG9pbnQgaXMgZGVzY3JpYmVkIGJ5IGl0cyB4IGFuZCB5IHZhbHVlLgoKV2UgbmVlZCB0byBwcm92aWRlIGEgbWFwcGluZyB0aGF0IHNwZWNpZmllcyB0aGUgZGF0YSBjb2x1bW5zJyBuYW1lIHRvIG1hcCB0byBhIHBvaW50J3MgdGhlIHggYW5kIHkgdmFsdWUKClRoYXQgbWFwcGluZyBpcyBkZWZpbmVkIGJ5IGFuIGFlc3RoZXRpY3MgZnVuY3Rpb25cCioqYWVzKCkqKgoKU2NhdHRlciBwbG90IGlzIHVzZWZ1bCB0byBleHBsb3JlIHRoZSByZWxhdGlvbiBvZiB0d28gdmFyaWFibGVzLgoKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsgIyBOT1RFOiAnKycgb3BlcmF0b3IgbXVzdCBiZSBwbGFjZWQgYXQgdGhlIGVuZCBvZiBhIGxpbmUKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkKYGBgCgojIyBVc2UgZ2VvbV9saW5lKCkgdG8gcmVwbGFjZSBnZW9tX3BvaW50KCkKZ2VvbV9wb2ludCgpIGFuZCBnZW9tX2xpbmUoKSByZXF1aXJlIHZlcnkgc2ltaWxhciBwYXJhbWV0ZXJzLlwKZ2VvbV9saW5lKCkgaXMgc2ltcGx5IGFuIGVuaGFuY2VkIHZpc3VhbGl6YXRpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGNvbm5lY3QgYWxsIHRoZSBwb2ludHMKCmBgYHtyfQojIGp1c3QgY2hhbmdlIGdlb21fcG9pbnQgdG8gZ2VvbV9saW5lIHdpdGhvdXQgY2hhbmdlIGFueXRoaW5nIGVsc2UKY2FycyAlPiUgZ2dwbG90KCkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9c3BlZWQsIHk9ZGlzdCkpIApgYGAKCgojIyBVc2UgZ2VvbV9zbW90aCgpIHRvIHByb2plY3QgYSBzbW9vdGggbGluZQphZ2FpbiBnZW9tX3Ntb290aCgpIGFuZCBnZW9tX3BvaW50KCkgcmVxdWlyZSB2ZXJ5IHNpbWlsYXIgcGFyYW1ldGVycy5cCmdlb21fc21vb3RoKCkgc21vb3RocyBvdXQgdGhlIGxpbmUgcHJvZ3Jlc3Npb24KCmBgYHtyfQojIGp1c3QgY2hhbmdlIGdlb21fcG9pbnQgdG8gZ2VvbV9saW5lIHdpdGhvdXQgY2hhbmdlIGFueXRoaW5nIGVsc2UKY2FycyAlPiUgZ2dwbG90KCkgKwogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkgCmBgYAoKIyMgR2VvbWV0cmljIE9iamVjdHMgQ29tcGFyaXNvbgphIGdlb20gaXMgdGhlIGdlb21ldHJpY2FsIG9iamVjdCB0aGF0IGEgcGxvdCB1c2UgdG8gcmVwcmVzZW50IGRhdGEuCldlIHVzZWQgcG9pbnRzLCBsaW5lcyBhbmQgc21vb3RoIGFib3ZlIG9uIHRoZSBzYW1lIGRhdGEgc2V0IGFuZCB0aGV5IHByb3ZpZGUgdmVyeSBkaWZmZXJlbnQgbWVzc2FnZS4gCgojIyBBZGRpbmcgTXVsdGlwbGUgTGF5ZXJzCmBgYHtyfQojIGp1c3QgY2hhbmdlIGdlb21fcG9pbnQgdG8gZ2VvbV9saW5lIHdpdGhvdXQgY2hhbmdlIGFueXRoaW5nIGVsc2UKY2FycyAlPiUgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PXNwZWVkLCB5PWRpc3QpKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4PXNwZWVkLCB5PWRpc3QpKSAKYGBgCgojIyBBZGRpbmcgQWVzdGhldGljcyB0byB5b3VyIFBsb3RzClVuLWNvbW1lbnQgdGhlIGV4dHJhIHBhcmFtZXRlcnMgdG8gYWRkIG1vcmUgYWVzdGhldGljcyB0byB5b3VyIHBsb3QKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSwKICAgICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIsICMgdGhlIGNvbG9yIG9mIGRhdGEgcG9pbnRzCiAgICAgICAgICAgICAjIHNpemUgPSAzLCAjIHRoZSBzaXplIG9mIGRhdGEgcG9pbnQKICAgICAgICAgICAgICMgYWxwaGEgPSAwLjUsICMgdGhlIHRyYW5zcGFyZW5jeSBvZiBkYXRhIHBvaW50cywgbWluPTAsIG1heD0xCiAgICAgICAgICAgICAjIHNoYXBlID0gMCwgIyB0aGUgc2hhcGUgb2YgZGF0YSBwb2ludAogICAgICAgICAgICAgKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQTE9UIFdJVEggT1VSIE9XTiBEQVRBCgojIyBMb2FkaW5nIERhdGE6IGFsbG93YW5jZQpgYGB7ciByZWFkaW5nIGRhdGEgZmlsZXN9CmFsbG93YW5jZSA9IHJlYWRfY3N2KCIuL2RhdGEvYWxsb3dhbmNlLmNzdiIpCnByaW50KGFsbG93YW5jZSkKYGBgCgoKIyMgQWxsb3dhbmNlIERhdGEgU2V0IGluIFNpbXBsZSBTY2F0dGVycGxvdApgYGB7ciBBbGxvd2FuY2UgU2NhdHRlcnBsb3R9CmFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KAogICAgbWFwcGluZz1hZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpLAogICAgY29sb3IgPSAib3JhbmdlIiwKICAgIHNpemUgPSAzCiAgICApIApgYGAKCgojIyBDb250aW51b3VzIFZhbHVlcyB2cy4gRGlzY3JldGUgVmFsdWVzCkNvbnRpbnVvdXMgdmFsdWVzIHJlZmVyIHRvIG51bWJlcnMgdmFsdWUgdGhhdCBoYXMgd2lkZSByYW5nZS4gRS5nLiBzYWxhcnksIGhlaWdodApEaXNjcmV0ZSB2YWx1ZXMgcmVmZXIgdG8gYSBsaW1pdGVkIG51bWJlciBvZiB2YWxpZCB2YWx1ZXMuICBJdCBjYW4gYmUgc3RyaW5nLiBJdCBjYW4gYmUgYSBmZXcgZGlzdGluY3QgbnVtYmVycy4KCldoZW4geW91IHByb2R1Y2UgcGxvdHMsIHBheSBhdHRlbnRpb24gdG8gd2hhdCB0eXBlIG9mIHZhbHVlIGFyZSByZXF1aXJlZCBieSB0aGUgZ2VvbXMuCgpJbiBtYW55IGNhc2VzLCB5b3Ugd2lsbCBuZWVkIHRvIGNvbnZlcnQgdGhlIGRhdGEgZmlyc3QuCioqbXV0YXRlKCkqKiBmdW5jdGlvbiBhcmUgcXVpdGUgb2Z0ZW4gdXNlZCBmb3IgdGhhdC4KCmV4YW1wbGU6CmBgYHtyfQphbGxvd2FuY2UgPSBhbGxvd2FuY2UgJT4lIAogIG11dGF0ZShBc3Nlc3NtZW50X1llYXIgPSBhcy5udW1lcmljKHN1YnN0cihBc3Nlc3NtZW50X1llYXIsIDEgLDQpKSkgCmBgYAoKCiMjIFNpbXBsZSBMaW5lIFBsb3QKYGBge3IgQWxsb3dhbmNlIExpbmUgR3JhcGh9CgojIFRoZSBmb2xsb3dpbmcgc3RhdGVtZW50IFdPTidUIGdlbmVyYXRlIGEgcGxvdAphbGxvd2FuY2UgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYykpCgojIEZvciBsaW5lIGdyYXBocywgdGhlIGRhdGEgcG9pbnRzIG11c3QgYmUgZ3JvdXBlZCBzbyB0aGF0IGl0IGtub3dzIHdoaWNoIHBvaW50cyB0byBjb25uZWN0LiAKIyBJbiB0aGlzIGNhc2UsIGFsbCBwb2ludHMgc2hvdWxkIGJlIGNvbm5lY3RlZCwgc28gZ3JvdXA9MS4gCiMgV2hlbiBtb3JlIHZhcmlhYmxlcyBhcmUgdXNlZCBhbmQgbXVsdGlwbGUgbGluZXMgYXJlIGRyYXduLCB0aGUgZ3JvdXBpbmcgZm9yIGxpbmVzIGlzIHVzdWFsbHkgZG9uZSBieSB2YXJpYWJsZS4KYWxsb3dhbmNlICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMsIGdyb3VwPTEsIGNvbG9yPSJPcmFuZ2UiKSkKYGBgCgojIyBBZGRpbmcgTXVsdGlwbGUgTGF5ZXJzIG9mIEdlb21ldHJ5CmBgYHtyfQphbGxvd2FuY2UgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYywgZ3JvdXA9MSwgY29sb3I9Ik9yYW5nZSIpKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpCgojIEFzIGJvdGggZ2VvbSB1c2UgdGhlIHNhbWUgZGF0YSBtYXBwaW5nLCB0aGUgYWJvdmUgc3RhdGVtZW50cyBjYW4gYmUgc2ltcGxpZmllZCBhcwphbGxvd2FuY2UgJT4lIGdncGxvdChhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMsIGdyb3VwPTEsIGNvbG9yPSJPcmFuZ2UiKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KHNpemU9NSkKYGBgCgojIyBVc2UgZ2VvbV9zbW9vdGgoKSB0byBzbW9vdGggb3V0IHRoZSBsaW5lCmBgYHtyfQphbGxvd2FuY2UgJT4lIGdncGxvdChhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMsIGdyb3VwPTEsIGNvbG9yPSJPcmFuZ2UiKSkgKwogIGdlb21fc21vb3RoKCkgKwogIGdlb21fcG9pbnQoc2l6ZT01KQpgYGAKCgoKIyMgU2F2ZSBhIFBsb3Q6IGdnc2F2ZSgpCmBgYHtyfQpteS5maXJzdC5wbG90ID0gYWxsb3dhbmNlICU+JSAKICBnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoCiAgICBtYXBwaW5nPWFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYyksCiAgICBjb2xvciA9ICJvcmFuZ2UiLAogICAgc2l6ZSA9IDMsCiAgICApIAoKcHJpbnQobXkuZmlyc3QucGxvdCkKCmdnc2F2ZSgiLi9vdXRwdXQvbXlfZmlyc3RfcGxvdC5wbmciKSAjIGRlZmF1bHQgaW1hZ2Ugc2l6ZQpnZ3NhdmUoIi4vb3V0cHV0L215X2ZpcnN0X3Bsb3RfbGFyZ2UucG5nIiwgd2lkdGg9NDAwMCwgaGVpZ2h0PTIwMDAsIHVuaXQ9InB4IikKCmBgYAoKIyMgQmFyIENoYXJ0IHdpdGggZ2VvbV9jb2woKQoKYGBge3J9CmFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmc9YWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljKSwKICAgICAgICAgICBmaWxsPSJ0b21hdG8iKSAKYGBgCgoKCgojIyBnZW9tX2JhcigpCmdlb21fYmFyKCkgaXMgdXNlZCBmb3IgY291bnRpbmcgdGhlIGZyZXF1ZW5jeSBvZiBlYWNoIG9jY3VycmVuY2Ugb2Ygb2JzZXJ2ZWQgdmFsdWUuCkl0J3MgdXN1YWxseSBmb3IgY291bnRpbmcgYSBsaW1pdCBzZXQgb2YgdmFsdWUKCmBgYHtyfQphbGxvd2FuY2UgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JhcihtYXBwaW5nPWFlcyh4PUJhc2ljKSkgIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDSEFMTEVOR0UKIyMgTXVsdGlwbGUgTGF5ZXJzIG9mIExpbmVzCmFkZCBsaW5lIHBsb3QgZm9yIGNvbHVtbiBvZiBDaGlsZAppbiB0aGUgc2FtZSBwbG90LCBhZGQgYW5vdGhlciBsaW5lIHBsb3QgZm9yIERlcGVuZGVudF9QYXJlbnRfNjAKYGBge3J9CmFsbG93YW5jZSAlPiUgZ2dwbG90KCkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUNoaWxkLCBncm91cD0xKSwgY29sb3I9Ik9yYW5nZSIpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9RGVwZW5kZW50X1BhcmVudF82MCwgZ3JvdXA9MSksIGNvbG9yPSJCbHVlIikgCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFdPUksgV0lUSCBNT1JFIENPTVBMRVggREFUQQoKIyMgTG9hZGluZyBEYXRhOiBncmFkdWF0ZXMuY3N2CmBgYHtyIHJlYWRpbmcgY29tcGxleCBkYXRhfQpncmFkdWF0ZXMgPSByZWFkX2NzdigiLi9kYXRhL2dyYWR1YXRlcy5jc3YiKQpwcmludChncmFkdWF0ZXMpCmBgYAoKIyMgU2ltcGxlIFNjYXR0ZXJwbG90CkxldCdzIGV4cGxvcmUgdGhlIGRhdGEgd2l0aCBzb21lIHNpbXBsZSBnZ3Bsb3QgcGxvdC4gCk92ZXJhbGwgdGhleSBhcmUgbm90IHZlcnkgdXNlZnVsLiAgSnVzdCBzb21lIHF1aWNrIGV4cGxvcmF0aW9uLgoKYGBge3J9CmdncGxvdChkYXRhPWdyYWR1YXRlcykgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50KSkKCmdncGxvdChkYXRhPWdyYWR1YXRlcykgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50LCBzaGFwZT1TZXgpICMgdXNlIHNoYXBlIHRvIGRpZmZlcmVudGlhdGUgZ3JvdXBzCiAgICAgICAgICAgICApCgpnZ3Bsb3QoZGF0YT1ncmFkdWF0ZXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgY29sb3I9U2V4KSAjIHVzZSBjb2xvciB0byBkaWZmZXJlbnRpYXRlIGdyb3VwcwogICAgICAgICAgICAgKQpgYGAKCiMjIFVzZSBmaWx0ZXIoKSB0byBFeHRyYWN0IFJlcXVpcmVkIFJvd3MKYGBge3IgdXNlIGZpbHRlcigpfQpncmFkdWF0ZXMgJT4lIApmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVW5kZXJncmFkdWF0ZSIsIFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiKSAlPiUKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50LCBjb2xvcj1TZXgpCikKYGBgCgoKIyMgVXNlIExpbmUgUGxvdCBUbyBFeHBsb3JlIHRoZSBUcmVuZGluZyAKVXNlIGxpbmUgcGxvdCB0byBleHBsb3JlIHRoZSB0cmVuZGluZyBvZiAiQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHN0dWRlbnQgaGVhZGNvdW50IHRyZW5kaW5nCgoKYGBge3J9CmxpYnJhcnkobWFncml0dHIpCmdyYWR1YXRlcyAlPD4lCiAgbXV0YXRlKEFjYWRlbWljWWVhcj1hcy5mYWN0b3IoQWNhZGVtaWNZZWFyKSwKICAgICAgICAgU2V4PWFzLmZhY3RvcihTZXgpCiAgICAgICAgICkgIyBjb252ZXJ0IHRoZSBBY2FkZW1pY1llYXIgYW5kIFNleCB0byBmYWN0b3IgdHlwZQoKZ3JhZHVhdGVzICU+JSAKZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBQcm9ncmFtbWVDYXRlZ29yeT09IkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IikgJT4lCmdncGxvdCgpICsKICBnZW9tX2xpbmUoCiAgICBtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgCiAgICAgICAgICAgICAgICAgICAgICAgICB5PUhlYWRjb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwPVNleCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1TZXgKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENIQUxMRU5HRQojIyBDb21wYXJpc29uIHdpdGggTGluZSBQbG90cwpVc2UgbGluZSBwbG90cyB0byBjb21wYXJlIGZlbWFsZSB1bmRlcmdyYWR1YXRlIHN0dWRlbnRzIGhlYWRjb3VudCB0cmVuZGluZyBpbiBQcm9ncmFtbWVDYXRlZ29yeSBvZiAiQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIGFuZCAiRW5naW5lZXJpbmcgYW5kIFRlY2hub2xvZ3kiClVzZSBmaWx0ZXIoKSB0byBleHRyYWN0IHJlcXVpcmVkIHJlY29yZApZb3UgY2FuIHVzZSBtdWx0aXBsZSBmaWx0ZXIoKSBjYWxsClVzZSAmLCB8IG9yIG11bHRpcGxlIGNvbmRpdGlvbnMKCgpgYGB7ciBQcmV2aWV3aW5nIFByb2dyYW1DYXRlZ29yeX0KZ3JhZHVhdGVzICU+JSAKICAuJFByb2dyYW1tZUNhdGVnb3J5ICU+JSAKICB1bmlxdWUoKSAjIGRpc3BsYXkgdGhlIHVuaXF1ZSBuYW1lcyBvZiBQcm9ncmFtbWVDYXRlZ29yeQoKZ3JhZHVhdGVzICU+JSAKICBmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVW5kZXJncmFkdWF0ZSIsIFNleD09IkYiKSAlPiUKICBmaWx0ZXIoUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCIgfCBQcm9ncmFtbWVDYXRlZ29yeT09IkVuZ2luZWVyaW5nIGFuZCBUZWNobm9sb2d5IikgJT4lIAogIHByaW50KCkgIyBUZXN0IGV4dHJhY3RpbmcgYW5kIHByaW50aW5nIHRoZSByZXF1aXJlZCByZWNvcmRzLgpgYGAKCgpgYGB7ciBCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCB2cy4gRW5naW5lZXJpbmcgYW5kIFRlY2hub2xvZ3l9CmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBTZXg9PSJGIikgJT4lCiAgZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKICBnZ3Bsb3QoCiAgICBhZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgeT1IZWFkY291bnQsCiAgICAgICAgICAgICBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgCiAgICAgICAgICAgICBjb2xvcj1Qcm9ncmFtbWVDYXRlZ29yeQogICAgICAgICAgICAgKQogICAgKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgCiAgCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBDSEFMTEVOR0U6IGxpbmUgcGxvdCBmb3IgaGlib3JfZml4aW5nXzFtCmBgYHtyfQpsaWJyYXJ5KGpzb25saXRlKSAjIGxvYWQgcGFja2FnZQpoa21hLmludGVyYmFuay51cmwgPSAiaHR0cHM6Ly9hcGkuaGttYS5nb3YuaGsvcHVibGljL21hcmtldC1kYXRhLWFuZC1zdGF0aXN0aWNzL2RhaWx5LW1vbmV0YXJ5LXN0YXRpc3RpY3MvZGFpbHktZmlndXJlcy1pbnRlcmJhbmstbGlxdWlkaXR5IgppbnRlcmJhbmsubGlxdWlkaXR5ID0gZnJvbUpTT04oaGttYS5pbnRlcmJhbmsudXJsKQojIHRoZSBhYm92ZSByZXRyaWV2YWwgd2lsbCB0YWtlIGEgd2hpbGUuICBUaGUgc2VydmVyIHJlc3BvbnNlIGlzIHNsb3cuCnN1bW1hcnkoaW50ZXJiYW5rLmxpcXVpZGl0eSkKc3RyKGludGVyYmFuay5saXF1aWRpdHkpCmludGVyYmFuay5saXF1aWRpdHkkcmVzdWx0CnN0cihpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCkKaW50ZXJiYW5rLnJlY29yZHMgPSBpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCRyZWNvcmRzICU+JSBhc190aWJibGUoKQppbnRlcmJhbmsucmVjb3JkcwoKaW50ZXJiYW5rLnJlY29yZHMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgbWFwcGluZz1hZXMoeD1lbmRfb2ZfZGF0ZSwgeT1oaWJvcl9maXhpbmdfMW0sIGdyb3VwPTEpLAogICAgY29sb3I9Im9yYW5nZSIKICAgICAgICAgICAgICkKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgR1JPVVBJTkcgQU5EIEFHR1JFR0FUSU9OCgojIyBVc2luZyBncm91cF9ieSgpIGFuZCBzdW1tYXJpc2UoKSAKYGBge3J9CmdyYWR1YXRlcyAlPiUgZ3JvdXBfYnkoQWNhZGVtaWNZZWFyLCBMZXZlbE9mU3R1ZHkpICU+JSAKICBzdW1tYXJpc2UoVG90YWxIZWFkY291bnQgPSBzdW0oSGVhZGNvdW50KSkgCgpncmFkdWF0ZXMgJT4lIGdyb3VwX2J5KEFjYWRlbWljWWVhciwgTGV2ZWxPZlN0dWR5KSAlPiUgCiAgc3VtbWFyaXNlKFRvdGFsSGVhZGNvdW50ID0gc3VtKEhlYWRjb3VudCkpICU+JSAKICBnZ3Bsb3QoCiAgICBhZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgeT1Ub3RhbEhlYWRjb3VudCwKICAgICAgICAgICAgIGdyb3VwPUxldmVsT2ZTdHVkeSwgCiAgICAgICAgICAgICBjb2xvcj1MZXZlbE9mU3R1ZHkKICAgICAgICAgICAgICkKICAgICkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV9wb2ludCgpIAogIApgYGAKCiMjIFVzZSBvZiBmaWx0ZXIoKQpVc2UgZmlsdGVyKCkgdG8ga2VlcCBvbmx5ICJUYXVnaHQgUG9zdGdyYWR1YXRlIiBSZWNvcmRzCgpUaGlzIHBsb3QgaXMgbm90IHZlcnkgdXNlZnVsIHdpdGhvdXQgcHJldmlvdXNseSBhcHBseWluZyBmaWx0ZXIoKSBhbmQgZ3JvdXBfYnkoKSBhbmQgc3VtbWFyaXNlKCkKCmBgYHtyfQpncmFkdWF0ZXMgJT4lIAogIGZpbHRlcihMZXZlbE9mU3R1ZHk9PSJUYXVnaHQgUG9zdGdyYWR1YXRlIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhcix5PUhlYWRjb3VudCwgZ3JvdXA9UHJvZ3JhbW1lQ2F0ZWdvcnksIGNvbG9yPVByb2dyYW1tZUNhdGVnb3J5KSkKYGBgCgoKIyMgZmlsdGVyKCkgKyBncm91cF9ieSgpICsgc3VtbWFyaXNlKCkKVXNlIGZpbHRlcigpIHRvIGV4dHJhY3QgcmVxdWlyZWQgcm93cwpVc2UgZ3JvdXBfYnkoKSBhbmQgc3VtbWFyaXNlKCkgdG8gZ3JvdXAgYW5kIGFnZ3JlYXRlIHRvdGFsIGhlYWRjb3V0IGZvciBib3RoIG1hbGUgYW5kIGZlbWFsZQpgYGB7ciBnZ3Bsb3QgbGluZX0KCmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlRhdWdodCBQb3N0Z3JhZHVhdGUiKSAlPiUgCiAgZ3JvdXBfYnkoQWNhZGVtaWNZZWFyLCBQcm9ncmFtbWVDYXRlZ29yeSkgJT4lIAogIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLHk9VG90YWxIZWFkY291bnQsIGdyb3VwPVByb2dyYW1tZUNhdGVnb3J5LCBjb2xvcj1Qcm9ncmFtbWVDYXRlZ29yeSkpCgojIEZvbGxvd2luZyBpcyB0aGUgc2FtZSBjaGFydCBmb3IgInVuZGVyZ3JhZHVhdGUiIAojIGdyYWR1YXRlcyAlPiUKIyAgIGZpbHRlcihMZXZlbE9mU3R1ZHk9PSJVbmRlcmdyYWR1YXRlIikgJT4lCiMgICBncm91cF9ieShBY2FkZW1pY1llYXIsIFByb2dyYW1tZUNhdGVnb3J5KSAlPiUKIyAgIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAlPiUKIyAgIGdncGxvdCgpICsKIyAgICAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLHk9VG90YWxIZWFkY291bnQsIGdyb3VwPVByb2dyYW1tZUNhdGVnb3J5LCBjb2xvcj1Qcm9ncmFtbWVDYXRlZ29yeSkpCiAgICAKYGBgCgoKCiMjIGdlb21fY29sKCkgZnVuY3Rpb24KCmBgYHtyfQpMZXZlbE9mU3R1ZHkgPSBncmFkdWF0ZXMgJT4lIC4kTGV2ZWxPZlN0dWR5ICU+JSB1bmlxdWUoKQpQcm9ncmFtbWVDYXRlZ29yeSA9IGdyYWR1YXRlcyAlPiUgLiRQcm9ncmFtbWVDYXRlZ29yeSAlPiUgdW5pcXVlKCkgCnByaW50KExldmVsT2ZTdHVkeSkKcHJpbnQoUHJvZ3JhbW1lQ2F0ZWdvcnkpCiAgCmdyYWR1YXRlcyAlPiUgCmZpbHRlcihQcm9ncmFtbWVDYXRlZ29yeT09IkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IikgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50LCBmaWxsPUxldmVsT2ZTdHVkeSkpCgpncmFkdWF0ZXMgJT4lIApmaWx0ZXIoUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgZmlsbD1MZXZlbE9mU3R1ZHkpKQoKYGBgCgojIyBNb3JlIEFnZ3JlZ2F0aW9uIEZ1bmN0aW9ucwpDZW50ZXI6IG1lYW4oKSwgbWVkaWFuKCkKU3ByZWFkOiBzZCgpLCBJUVIoKSwgbWFkKCkKUmFuZ2U6IG1pbigpLCBtYXgoKSwgcXVhbnRpbGUoKQpQb3NpdGlvbjogZmlyc3QoKSwgbGFzdCgpLCBudGgoKSwKQ291bnQ6IG4oKSwgbl9kaXN0aW5jdCgpCkxvZ2ljYWw6IGFueSgpLCBhbGwoKQoKTW9yZSBpbmZvcm1hdGlvbiBhdApbc3VtbWFyaXNlKCkgZnVuY3Rpb25dKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc3VtbWFyaXNlLmh0bWwpCgoKIyMgZ2VvbV9iYXIoKSBmdW5jdGlvbgpiYXIgY2hhcnQgZ2l2ZSB0aGUgY291bnRpbmcgZnJlcXVlbmN5IChudW1iZXIgb2YgcmVjb3JkIGluIHRoZSBkYXRhIHNldCkKYGBge3J9CgpncmFkdWF0ZXMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIpKSAjIHlvdSBvbmx5IG5lZWQgdG8gcHJvdmlkZSB0aGUgeCBheGlzCmBgYAoKIyMgYm94IHBsb3QKVGhlIGJveHBsb3QgY29tcGFjdGx5IGRpc3BsYXlzIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlLlxuCkl0IHZpc3VhbGlzZXMgZml2ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgKHRoZSBtZWRpYW4sIHR3byBoaW5nZXMgYW5kIHR3byB3aGlza2VycyksIGFuZCBhbGwgIm91dGx5aW5nIiBwb2ludHMgaW5kaXZpZHVhbGx5LgoKYGBge3J9CmdyYWR1YXRlcyAlPiUgCmdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9U2V4LCB5PUhlYWRjb3VudCkpCgpncmFkdWF0ZXMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmc9YWVzKHg9TGV2ZWxPZlN0dWR5LCB5PUhlYWRjb3VudCkpCmBgYAoKIyMgZmFjZXRfd3JhcCgpCmZhY2V0cyBhcmUgdXNlZnVsIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4KSXQgc3BsaXQgeW91ciBwbG90IGludG8gc3VicGxvdCAoYS5rLmEgZmFjZXRzKSB0aGF0IGVhY2ggZGlzcGxheSBvbmUgc3Vic2V0IG9mIHRoZSBkYXRhLgoKZmFjZXRfd3JhcCgpIGxldHMgeW91IHdvcmsgd2l0aCBPTkUgZXh0cmEgdmFyaWFibGUgKGJlc2lkZXMgeCBhbmQgeSkuICAKRWFjaCBjYXRlZ29yaWNhbCB2YWx1ZSB3aWxsIHVzZWQgdG8gcHJvZHVjZSB0byBhIHN1YiBwbG90LgpXZSB1c2UgTGV2ZWxPZlN0dWR5IGhlcmUuICBTaW5jZSB0aGVyZSBhcmUgRk9VUiBkaXN0aW5jdCB2YWx1ZXMsIHlvdSB3aWxsIHNlZSBGT1VSIHN1Yi1wbG90cwpgYGB7cn0KZ3JhZHVhdGVzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCkpICsKICBmYWNldF93cmFwKH4gTGV2ZWxPZlN0dWR5KSAjIFlvdSB3aWxsIHNlZSBGT1VSIHN1YiBwbG90cyBhcyB0aGVyZSBhcmUgRk9VUiBkaXN0aW5jdCB2YWx1ZXMgZm9yIHRoaXMgdmFyaWFibGUuCmBgYAoKIyMgZmFjZXRfZ3JpZCgpCmZhY2V0X2dyaWQoKSBsZXRzIHlvdSB3b3JrIHdpdGggVFdPIGV4dHJhIHZhcmlhYmxlcyAoYmVzaWRlcyB4IGFuZCB5KS4gIApFYWNoIGNvbWJpbmF0aW9uIG9mIHRoZXNlIHR3byB2YXJhaWJsZXMnIHZhbHVlIGFyZSB1c2VkIHRvIHByb2R1Y2UgdG8gYSBzdWIgcGxvdC4KCllvdSBzaG91bGQgU0VFIDI4IHN1YiBwbG90cyBhcyB0aGVyZSBhcmUgRk9VUiBkaXN0aW5jdCB2YWx1ZXMgZm9yIExldmVsT2ZTdHVkeSBhbmQgNyBkaXN0aW5jdCB2YWx1ZXMgZm9yIFByb2dyYW1tZUNhdGVnb3J5LgpgYGB7cn0KZ3JhZHVhdGVzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCkpICsKICBmYWNldF9ncmlkKExldmVsT2ZTdHVkeSB+IFByb2dyYW1tZUNhdGVnb3J5KQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNQUtFIElUIFBSRVRUWQpVc2Ugb2YgdGl0bGUsIGxhYmVsLCBiYWNrZ3JvdW5kIGNvbG9yIGFuZCB0aGVtZXMKCmBgYHtyfQojIGluIHRoaXMgZXhhbXBsZSB3ZSBzYXZlIHRoZSBwbG90IHRvIGEgdmFyaWFibGUgbmFtZSAnbGV2ZWwuYmFyLnBsb3QnIHNvIHRoYXQgd2UgY2FuIHVzZSBpdCBhZ2FpbiBhbmQgYWdhaW4uCmxldmVsLmJhci5wbG90ID0gZ3JhZHVhdGVzICU+JSAKZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iRW5naW5lZXJpbmcgYW5kIFRlY2hub2xvZ3kiKSAlPiUgCmdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgeT1IZWFkY291bnQsIGZpbGw9TGV2ZWxPZlN0dWR5KSkKCiMgVG8gc2hvdyB0aGUgcGxvdCwganVzdCB1c2UgcHJpbnQoKSBmdW5jdGlvbiB3aXRoIHRoZSBwcmV2aW91cyBzYXZlZCBwbG90IHZhcmlhYmxlIGFzIHBhcmFtZXRlci4KcHJpbnQobGV2ZWwuYmFyLnBsb3QpCmBgYAoKCiMjIFBsb3QgQmFja2dyb3VuZAoqKmVsZW1lbnRfcmVjdCgpKiogaXMgYSBmdW5jdGlvbiB0byBnZW5lcmF0ZWQgcmVjdGFuZ2xlIGdlb21ldHJ5IGVsZW1lbnQuCllvdSBoYXZlIHRvIHNwZWNpZnkgdGhlICoqZmlsbCoqIHBhcmFtZXRlciBieSBjb2xvciBuYW1lIG9yIGhleCBjb2RlIGNvZGUgYnkgc3RyaW5nCgpQbG90IEJhY2tncm91bmQgcmVmZXJzIHRvIHRoZSBiaWcgYXJlYSBvZiBldmVyeXRoaW5nIHJlbGV2YW50IHRvIHRoZSBwbG90LgoKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJvcmFuZ2UiKSkgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQKYGBgCgojIyBQYW5lbCBCYWNrZ3JvdW5kClBhbmVsIGJhY2tncm91bmQgcmVmZXJzIHRvIHRoZSBpbm5lciBhcmVhIG9mIHBsb3QuIEFyZWEgZm9yIHNob3dpbmcgaGVhZGVyLCBheGlzIGxhYmxlIGFuZCBsZWdlbmQgYXJlIE5PVCBpbmNsdWRlZC4KYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0ib3JhbmdlIikpICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZApgYGAKCiMjIFJlbW92ZSBQbG90IGFuZCBQYW5lbCBCYWNrZ3JvdW5kCkluIHZpc3VhbCBkZXNpZ24sIGNvbG9yIGlzIHZlcnkgcG93ZXJmdWwgdG9vbCB0byBndWlkZSB1c2VycycgYXR0ZW50aW9uLiBCdXQgeW91IGhhdmUgdG8gdXNlIHRoZW0gY2FyZWZ1bGx5LgoKVG9vIG1hbnkgY29sb3JzIHdpbGwgdXN1YWxseSBkbyB0aGUgb3Bwb3NpdGUgLSBjb25mdXNlIHRoZSBhdWRpZW5jZS4KTWluaW1hbCBkZXNpZ24gaXMgdGhlIHJlY2VudCB0cmVuZC4gIEV4cGVjaWFsbHkgdHJ1ZSB3aGVuIG1hbnkgYXJlIHVzaW5nIHNtYWxsIGRldmljZSBsaWtlIG1vYmlsZSBwaG9uZSBmb3IgZGF5LXRvLWRheSBjb21tdW5pY2F0aW9uLgoKSW4gdGhpcyBleGFtcGxlLCB3ZSBhcmUgcmVtb3ZpbmcgYm90aCBwbG90IGFuZCBwYW5lbCBiYWNrZ3JvdW5kIHRvIGFjaGlldmUgYSBjbGVhbiBkZXNpZ24uICBBZnRlciBhbGwsIGJhY2tncm91bmQgaXMgdGhlIG1haW4gZGlzaC4gVmVyeSBvZnRlbiBiYWNrZ3JvdW5kIGNvbG9yIGNhdXNlcyBkaXN0cmFjdGlvbiB0byBncmFwaC4KCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgIyBzdHlsaW5nIHRoZSBncmlkIGxpbmUgZm9yIHktYXhpcwpgYGAKCiMjIENoYW5nZSBMYWJlbCBmb3IgeC95IEF4aXMKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICMgTGFiZWwgZm9yIFggYXhpcwpgYGAKCiMjIFJhdGF0ZSB0aGUgTGFiZSBUZXh0CmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQpgYGAKCgoKIyMgQ2hhbmdlIEZpbGwgQ29sb3JzCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwdXJwbGUiLCAib3JhbmdlIiwgImJsdWUiLCAidG9tYXRvIikpICMgdXNlIGMoKSBmdW5jdGlvbiB0byBzcGVjaWZ5IGNvbG9yIGxpc3QKYGBgCgojIyBTdHlsaW5nIFRoZSBMZWdlbmRzCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwdXJwbGUiLCAib3JhbmdlIiwgImJsdWUiLCAidG9tYXRvIiksCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQodGl0bGU9IkxldmVsIG9mIFN0dWR5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikKICAgICAgICAgICAgICAgICAgICApICMgbW92ZSBsZWdlbmQgcG9zaXRpb24gdG8gdG9wIGFuZCBsYWJlbCBwb3NpdGlvbiB0byBib3R0b20gCiAgCmBgYAoKIyMgQWRkIFRpdGxlIGFuZCBTdWJ0aXRsZQpgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBhbmVsIGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBsb3QgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5IikpICsgIyBzdHlsaW5nIHRoZSBncmlkIGxpbmUgZm9yIHktYXhpcwogIHlsYWIoIk51bWJlciBvZiBTdHVkZW50IikgKyAjIExhYmVsIGZvciBZIGF4aXMKICB4bGFiKCJZZWFyIikgKyAjIExhYmVsIGZvciBYIGF4aXMKICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygicHVycGxlIiwgIm9yYW5nZSIsICJibHVlIiwgInRvbWF0byIpLAogICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJMZXZlbCBvZiBTdHVkeSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnBvc2l0aW9uID0gImJvdHRvbSIpCiAgICAgICAgICAgICAgICAgICAgKSArICMgbW92ZSBsZWdlbmQgcG9zaXRpb24gdG8gdG9wIGFuZCBsYWJlbCBwb3NpdGlvbiB0byBib3R0b20gCiAgZ2d0aXRsZSgiSG9uZyBLb25nIEhpZ2hlciBFZHVjYXRpb24gU3R1ZGVudCBIZWFkY291bnQiLCBzdWJ0aXRsZT0iMjAwOSAtIDIwMTkiKQpgYGAKCiMjIEFkZCBBbm5vdGF0aW9ucyBUZXh0cwpBZGQgZXh0cmEgdGV4dHMvc2hhcGUgdG8gZW5oYW5jZSB5b3VyIHZpc3VhbGl6YXRpb24KYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsPSJSZWNvcmRcbkhpZ2giLCB4PSIyMDE3LzE4IiwgeT01MzAwKSAjIHlvdSBjYW4gY2hhbmdlIHRleHQgcG9zaXRpb24gdmFsdWUgb2YgeCBhbmQgeSB0byBzZXQgdGhlIHRleHQgcG9zaXRpb24KYGBgCgojIyBBZGRpbmcgUmVmZXJlbmNlIExpbmVzCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwdXJwbGUiLCAib3JhbmdlIiwgImJsdWUiLCAidG9tYXRvIiksCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQodGl0bGU9IkxldmVsIG9mIFN0dWR5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikKICAgICAgICAgICAgICAgICAgICApICsgIyBtb3ZlIGxlZ2VuZCBwb3NpdGlvbiB0byB0b3AgYW5kIGxhYmVsIHBvc2l0aW9uIHRvIGJvdHRvbSAKICBnZ3RpdGxlKCJIb25nIEtvbmcgSGlnaGVyIEVkdWNhdGlvbiBTdHVkZW50IEhlYWRjb3VudCIsIHN1YnRpdGxlPSIyMDA5IC0gMjAxOSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCBsYWJlbD0iUmVjb3JkXG5IaWdoIiwgeD0iMjAxNy8xOCIsIHk9NTMwMCkgKyAjIHlvdSBjYW4gY2hhbmdlIHRleHQgcG9zaXRpb24gdmFsdWUgb2YgeCBhbmQgeSB0byBzZXQgdGhlIHRleHQgcG9zaXRpb24KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MzIwMCkgKyAjIGFkZHMgaG9yaXpvbnRhbCBsaW5lCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gIjIwMTcvMTgiLCBjb2xvcj0id2hpdGUiKSAjIGFkZHMgdmVydGljYWwgbGluZQogIApgYGAKCgojIyBVc2luZyBUaGVtZXMKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX2J3KCkgIyBibGFjayBhbmQgd2hpdGUgdGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9taW5pbWFsKCkgIyBibGFjayBhbmQgd2hpdGUgdGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9kYXJrKCkgIyBibGFjayBhbmQgd2hpdGUgdGhlbWUKYGBgCgojIyAgTW9yZSAzcmQtcGFydHkgVGhlbWVzCkluc3RhbGwgKipnZ3RoZW1lcyoqIHBhY2thZ2UgdG8gdW5sb2NrIHdpZGVyIHNlbGVjdGlvbnMgb2YgdGhlbWVzLgoKYGBge3J9CmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpICMgY2hlY2sgaWYgcGFjbWFuIGFscmVhZHkgaW5zdGFsbGVkLiBJZiBub3QsIGluc3RhbGwgaXQuCnBhY21hbjo6cF9sb2FkKGdndGhlbWVzKQoKbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWVfZXhjZWwoKSArICMgRXhjZWwgVGhlbWUKICBnZ3RpdGxlKCJFeGNlbCBUaGVtZSIpCiAgCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWVfd3NqKCkgKyAjIFdhbGwgU3RyZWV0IEpvdXJuYWwgVGhlbWUKICBnZ3RpdGxlKCJXYWxsIFN0cmVldCBKb3VybmFsIFRoZW1lIikKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9lY29ub21pc3QoKSArICMgRWNvbm9taXN0IFRoZW1lCiAgZ2d0aXRsZSgiRWNvbm9taXN0IFRoZW1lIikKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArICMgRml2ZSBUaGlydHkgRWlnaHQKICBnZ3RpdGxlKCJGaXZlIFRoaXJ0eSBFaWdodCBUaGVtZSIpCgpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTU9SRSBSRVNPVVJDRVMgT04gZ2dwbG90MgoKIyMgb2ZmaWNpYWwgd2Vic2l0ZQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8iKQpgYGAKCiMjIGV4dGVuc2lvbnMgZ2FsbGVyeQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL2V4dHMuZ2dwbG90Mi50aWR5dmVyc2Uub3JnL2dhbGxlcnkiKQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTU9ERUxTCgohW1dvcmsgRm9sbG93IG9mIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC9lNWJmMmE4ZjRjNzg3YTEyZmFjYmMwYjQxOTFmYzgyYmQxOTJmNGM1LzRlNWQyL2RpYWdyYW1zL2RhdGEtc2NpZW5jZS1tb2RlbC5wbmcpCgpEYXRhIFNjaWVuY2UgaXMgY29tYmluYXRpb24gb2YgZWZmb3J0cyBhbmQgcmVzdWx0cyBvZiBwcm9ncmFtbWluZywgbWF0aGVtYXRpY3MgYW5kIGRvbWFpbiBleHBlcnRpc2UuICBBbW9uZyBhbGwsIG1hdGhlbWF0aWNzIGlzIHRoZSBmb3VuZGF0aW9uIG9mIG1vZGVscy4gIFdpdGggbW9kZWxzLCBkYXRhIHNjaWVudGlzdHMgbWFrZSBwcmVkaWN0aW9uczsgZGlzY292ZXIgaGlkZGVuIHBhdHRlcm5zOyBhbmQgY29uY2x1ZGUgaW5zaWdodHMuIAoKTW9kZWxpbmcgaXMgdXN1YWxseSBhbiBpdGVyYXRpdmUgcHJvY2VzcyBhbW9uZyBkYXRhIHRyYW5zZm9ybWF0aW9uLCBkYXRhIHZpc3VhbGl6YXRpb24sIGV4cGxvcmluZyB3aXRoIG1vZGVscyBhbmQgZml0dGluZy4KCiMjIFdoYXQgZXhhY3RseSBpcyBhIE1vZGVsPwoKSHVtYW4gYXJlIGdvb2QgaW4gZHJhd2luZyBjb25jbHVzaW9ucyBhbmQgcHJvdmlkaW5nIGluc2lnaHQgd2hpbGUgYXJlIE5PVCBnb29kIGluIGRpcmVjdGx5IGZhY2luZyBsYXJnZSBudW1iZXIgb2YgZGF0YSBhdHRyaWJ1dGVzIGFuZCBodWdlIHZvbHVtZSBvZiByYXcgZGF0YS4gIAoKIVtNb2RlbHMgRXhhbXBsZV0oaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC9lMjhhNjZhZGY2ZThiMmQxMjdkYjhkM2FmOWFjOTkyYTJhYmI4N2NlLzQ3MzA4L21vZGVsLWJhc2ljc19maWxlcy9maWd1cmUtaHRtbC91bm5hbWVkLWNodW5rLTQ1LTEucG5nKQoKQSBtb2RlbCBpcyBtYXRoZW1hdGljcyBleHByZXNzaW9uIHRoYXQgcHJvdmlkZXMgYSBzaW1wbGUgbG93LWRpbWVuc2lvbmFsICoqc3VtbWFyeSoqIG9mIGEgZGF0YSBzZXQgc28gdGhhdCB3ZSBjYW4gZHJhdyBjb25jbHVzaW9uIGFuZCBldmVuIHByb3ZpZGluZyBpbnNpZ2h0cy4gIE1vZGVscyBvbmx5IHByb3ZpZGUgYXBwcm94aW1hdGlvbiAoTk9UIHRoZSBleGFjdCB0cnV0aCkuCgoKIyMgQmFzaWMgQ29uY2VwdHMgb2YgTW9kZWwKCkxldCdzIGRvIHNvbWUgc2ltcGxlIFIgY29kaW5nIHRvIHVuY292ZXIgdGhlIGJhc2ljIGNvbmNlcHQgb2YgbW9kZWwKCmBgYHtyIExvYWRpbmcgUmVxdWlyZWQgUGFja2FnZXMgfQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKSAjIGluc3RhbGwgcGFjbWFuCnBhY21hbjo6cF9sb2FkKHBhY21hbiwgdGlkeXZlcnNlLCBtb2RlbHIsIG1hZ3JpdHRyKSAjIGluc3RhbGwgKG9yIGxvYWQpIHJlcXVpcmVkIHBhY2thZ2VzCmBgYAoKCkxldCdzIHVzZSBhIHNpbXBsZSBidWlsdC1pbiBkYXRhIHNldCAqKnNpbTEqKiBmb3IgZXhwbG9yaW5nLgpJbiB0aGlzIHNpbXVsYXRpb24gZGF0YSB5b3UgY2FuIHN0cm9uZ2x5IHNlZSB0aGUgcGF0dGVybiB3aXRoIHRoZSBoZWxwIG9mIHNpbXBsZSBzY2F0dGVyLXBsb3QuCgpgYGB7ciBVc2luZyBzaW0xIERhdGEgU2V0fQpwX2RhdGEobW9kZWxyKSAjIGRpc3BsYXkgYWxsIHRoZSBidWlsdC1pbiBkYXRhIHNldHMgb2YgbW9kZWxyCnByaW50KGhlaWdodHMpCj9oZWlnaHRzCnByaW50KHNpbTEpICMgY29udGFpbnMgdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzOiB4LCB5CgpnZ3Bsb3Qoc2ltMSwgYWVzKHgsIHkpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgR2VuZXJhdGluZyBhIFJhbmRvbSBMaW5lYXIgTW9kZWwKTGluZWFyIGxpbmUgYW5kIHF1YWRyYXRpYyBjdXJ2ZSBhcmUgd2lkZWx5IHVzZWQgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb24gb2YgdHdvIHZhcmlhYmxlcy4gTGV0J3MgdGFrZSBsaW5lYXIgbW9kZWwgYXMgc2ltcGxlIGV4YW1wbGUgdG8gZ3JhYiB0aGUgZXNzZW5jZSBvZiBtb2RlbC4KCkEgbGluZWFyIG1vZGVsIGlzIGRlc2NyaWJlZCBhcwp5ID0gYTEgKyB4ICogYTIKeCBhbmQgeSBhcmUgdGhlIHZhcmlhYmxlIGZyb20gZGF0YXNldAphMSBhbmQgYTIgYXJlIHBhcmFtZXRlcnMgdGhhdCBjYW4gdmFyeSB0byBjYXB0dXJlIGRpZmZlcmVudCBwYXR0ZXJucy4KCkxldCdzIGdlbmVyYXRlIGEgcmFuZG9tIHZhbHVlIG9mIGExIGFzICoqaW50ZXJjZXB0KiogYW5kIGEyIGFzICoqc2xvcGUqKi4KSGVyZSwgd2UgdXNlICoqcnVuaW5mKCkqKiB0byBnZW5lcmF0ZSBhIHJhbmRvbSB1bmlmb3JtIGRpc3RyaWJ1dGVkIG51bWJlcgoKKipOb3RlKio6IApZb3UgbWlnaHQgaGF2ZSB0byByZXBlYXRpdmVseSBydW4gYSBmZXcgdGltZXMgYmVmb3JlIHlvdSBjYW4gc2VlIHRoZSB2aXN1YWxpemVkIHJhbmRvbSBtb2RlbCByZXByZXNlbnRlZCBieSBhIG9yYW5nZSBzdHJhaWdodCBsaW5lLgpgYGB7cn0KbW9kZWwgPSB0aWJibGUoCiAgYTEgPSBydW5pZigxLCAtMjAsIDQwKSwgIyByYW5kb20gaW50ZXJjZXB0IHZhbHVlIGJldHdlZW4gLTIwIHRvIDQwCiAgYTIgPSBydW5pZigxLCAtNSwgNSkgIyByYW5kb20gc2xvcCB2YWx1ZSBiZXR3ZWVuIC01IHRvIDUKKQojIHByaW50KG1vZGVsKSAjIHVuLWNvbW1lbnQgdG8gcHJpbnQgdGhlIHJhbmRvbSBtb2RlbAoKZ2dwbG90KHNpbTEsIGFlcyh4LHkpKSArCiAgZ2VvbV9wb2ludCgpICsgIyB0aGlzIHBsb3RzIGFsbCB0aGUgZGF0YQogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBhMSwgc2xvcGUgPSBhMiksIAogICAgICAgICAgICAgIGRhdGE9bW9kZWwsIAogICAgICAgICAgICAgIGNvbG9yPSJPcmFuZ2UiCiAgICAgICAgICAgICAgKSAjIHRoaXMgYWRkIHRoZSBzdHJhaWdodCBsaW5lLCBhLmsuYSwgb3VyIHJhbmRvbSBtb2RlbCBvbiB0b3AgdG8gZGF0YSBsYXllcgpgYGAKCiMjIEdlbmVyYXRpbmcgMjUwIFJhbmRvbSBNb2RlbHMgYXMgQ2FuZGlkYXRlIE1vZGVscwpUaGUgbnVtYmVyIG9mIHBvdGVudGlhbCBtb2RlbHMgYXJlIHVubGltaXRlZC4gIExldCdzIHRyeSB0byBnZW5lcmF0ZSAyNTAgcmFuZG9tIG9uZXMgYXMgY2FuZGlkYXRlIG1vZGVscy4gIApBbW9uZyB0aGVzZSAyNTAgbW9kZWxzLCBzb21lIGFyZSB2ZXJ5IGJhZCB0aGF0IHlvdSBjYW4ganVkZ2UgZXZlbiBieSBnbGFuY2luZy4gU29tZSBhcmUgbm90IGJhZCBidXQgd2UgZG9uJ3Qga25vdyB3aGljaCBvbmUgaXMgdGhlIGJlc3QgYW1vbmcgdGhlbS4KYGBge3J9Cm1vZGVscyA9IHRpYmJsZSgKICBhMSA9IHJ1bmlmKDI1MCwgLTIwLCA0MCksICMgMjUwIHJhbmRvbSBpbnRlcmNlcHQgdmFsdWVzIGJldHdlZW4gLTIwIHRvIDQwCiAgYTIgPSBydW5pZigyNTAsIC01LCA1KSAjIDI1MCByYW5kb20gc2xvcCB2YWx1ZXMgYmV0d2VlbiAtNSB0byA1CikKCiMgbGV0IGFkZCB0aGVzZSByYW5kb20gbGluZWFyIG1vZGVscyBhcyBvdmVybGF5IG9uIHRvcCBvZiBkYXRhIApnZ3Bsb3Qoc2ltMSwgYWVzKHgseSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBhMSwgc2xvcGUgPSBhMiksIGRhdGE9bW9kZWxzLCBhbHBoYT0wLjIpCmBgYAojIyBTZWxlY3RpbmcgdGhlIE1vc3QgRml0dGluZyBUZW4gTW9kZWxzCmBgYHtyfQojIHRoaXMgZnVuY3Rpb24gY2FsY3VsYXRlcyB0aGUgbW9kZWxlZCB5IHZhbHVlIG9mIGVhY2ggZ2l2ZW4gJ3gnIHZhbHVlCm1vZGVsZWRfeSA9IGZ1bmN0aW9uKGEsIGRhdGEpIHsKICBhWzFdICsgZGF0YSR4ICogYVsyXSAjIGFbMV0gaXMgdGhlIGludGVyY2VwdCBhbmQgYVsyXSBpcyB0aGUgc2xvcGUKfQojIG1vZGVsZWRfeShjKDcsIDEuNSksIHNpbTEpICMgdGVzdC1ydW4gbW9kZWxlZF95IGZ1bmN0aW9uCgojIHRoaXMgZnVuY3Rpb24gY2FsY3VsYXRlcyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBhbiBhY3R1YWwgeSB2YWx1ZSBhbmQgdGhlIHByZWRpY3RlZCB5IHZhbHVlIChtb2RlbGVkX3kpCm1lYXN1cmVfZGlzdGFuY2UgPSBmdW5jdGlvbihtb2QsIGRhdGEpIHsKICBkaWZmIDwtIGRhdGEkeSAtIG1vZGVsZWRfeShtb2QsIGRhdGEpICMgbW9kIGlzIHJhbmRvbSBpbnRlcmNlcHQgYW5kIHNsb3BlIG9mIGEgY2VydGFpbiBtb2RlbAogIHNxcnQobWVhbihkaWZmIF4gMikpICMgcm9vdC1tZWFuLXNxdWFyZWQgZGV2aWF0aW9uIHRvIGNvbXB1dGUgb3ZlcmFsbCBkaXN0YW5jZQp9CiMgbWVhc3VyZV9kaXN0YW5jZShjKDcsIDEsNSksIHNpbTEpICMgdGVzdC1ydW4gbWVhc3VyZV9kaXN0YW5jZSBmdW5jdGlvbgoKIyB0aGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlICdvdmVyYWxsJyBkaXN0YW5jZSBmb3IgYSBnaXZlbiBtb2RlbCB3aXRoIGExIGFzIGludGVyY2VwdCBhbmQgYTIgYXMgc2xvcGUKc2ltMV9kaXN0ID0gZnVuY3Rpb24oYTEsIGEyKSB7CiAgbWVhc3VyZV9kaXN0YW5jZShjKGExLCBhMiksIHNpbTEpICMgYTEgaXMgdGhlIGludGVyY2VwdCBvZiBhIG1vZGVsIHdoaWxlIGEyIGlzIHRoZSBzbG9wZQp9CgojIHVzZSBtYXAyX2RibCAoYSBtYXBwaW5nIGZ1bmN0aW9uKSB0byBhIG5ldyBjb2x1bW4gbmFtZWQgJ2Rpc3QnIHRvIGVhY2ggcmFuZG9tIG1vZGVsCm1vZGVscyAlPD4lIAogIG11dGF0ZShkaXN0ID0gcHVycnI6Om1hcDJfZGJsKGExLCBhMiwgc2ltMV9kaXN0KSkKbW9kZWxzICMgbm93IHdlIGhhdmUgYW4gZXh0cmEgY29sdW1uIG5hbWVkICdkaXN0JyBpbiBvdXIgbW9kZWxzCgojIHBsb3R0aW5nIHRoZSBiZXN0IDEwIG1vZGVscyBmcm9tIHRoZSByYW5rZWQgZGlzdGFuY2VzCmdncGxvdChzaW0xLCBhZXMoeCwgeSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3VyID0gImdyZXkzMCIpICsgCiAgZ2VvbV9hYmxpbmUoCiAgICBhZXMoaW50ZXJjZXB0ID0gYTEsIHNsb3BlID0gYTIsIGNvbG9yID0gLWRpc3QpICwgCiAgICBkYXRhID0gZmlsdGVyKG1vZGVscywgcmFuayhkaXN0KSA8PSAxMCkgIyBUbyBzaG93IG9ubHkgdGhlIGJlc3QgNSwgY2hhbmdlIDEwIHRvIGZpdmUKICApCgpgYGAKCiMjIFVzaW5nIGxtKCkgZnVuY3Rpb24KSWYgdGhlIHByZXZpb3VzIFIgY29kZXMgb24gY2hvb3NpbmcgYmVzdCAxMCBhbW9uZyAyNTAgcmFuZG9tIG1vZGVscyBhcmUgdG9vIG11Y2ggZGlnZXN0LiBJdCdzIGZpbmUuICBJdCdzIGp1c3QgZm9yIHlvdSB0byBmZWVsIHRoZSBwcm9jZXNzIGFuZCBlc3NlbmNlIG9mIG1vZGVscyBhbmQgZml0dGluZyBtb2RlbHMuCgpJbiBmYWN0LCBSIG1ha2VzIGxpbmVhciBtb2RlbCBmaXR0aW5nIGV4dHJlbWVseSBlYXN5IGJ5IGp1c3Qgb25lIHNpbmdsZSBsaW5lIG9mIGZ1bmN0aW9uIGNhbGxpbmcgdG8gdGhlICoqbG0oKSoqIGZ1bmN0aW9uIChhIGJ1aWx0LWluIGxpbmVhciBtb2RlbCBmaXR0aW5nIGZ1bmN0aW9uKQoKKipsbSgpKiogZmluZHMgdGhlIGNsb3Nlc3QgbW9kZWwgaW4gYSBzaW5nbGUgc3RlcCwgdXNpbmcgYSBzb3BoaXN0aWNhdGVkIGFsZ29yaXRobSB0aGF0IGludm9sdmVzIGdlb21ldHJ5LCBjYWxjdWx1cywgYW5kIGxpbmVhciBhbGdlYnJhLgoKKipsbSgpKiogaGFzIGEgc3BlY2lhbCB3YXkgdG8gc3BlY2lmeSB0aGUgbW9kZWwgZmFtaWx5OiBmb3JtdWxhcy4gCkZvcm11bGFzIGxvb2sgbGlrZSAqKnkgfiB4KiosIHdoaWNoIGxtKCkgd2lsbCB0cmFuc2xhdGUgdG8gYSBmdW5jdGlvbiBsaWtlIHkgPSBhMSArIGEyICogeApgYGB7cn0KIyByZWd1bGFyIGZ1bmN0aW9uIGNhbGxpbmcgbWFubmVyCnNpbTFfYXV0b19tb2RlbCA9IGxtKHkgfiB4LCBkYXRhID0gc2ltMSkgIyBmaW5kaW5nIHRoZSBvcHRpbWl6ZWQgbGluZWFyIG1vZGVsCiMgT3IgdXNpbmcgcGlwaW5nIGJlbG93CnNpbTFfYXV0b19tb2RlbCA9IHNpbTEgJT4lICBsbSh5IH4geCwgLikgIyBzaW5jZSBwaXBpbmcgYWx3YXlzIGFzc3VtZXMgb3V0cHV0cyBmcm9tIHByZXZpb3VzIGZ1bmN0aW9uIGNhbGwgd2lsbCBiZSB0aGUgZmlyc3QgcGFyYW1ldGVyIG9mIG5leHQgZnVuY3Rpb24sIGhlcmUgd2UgdXNlICIuIiB0byBpbmRpY2F0ZSBzaW0xIHdpbGwgYmUgZmVkIGFzIHNlY29uZCBwYXJhbWV0ZXIKCnByaW50KHNpbTFfYXV0b19tb2RlbCkgIyBwcmludCBvdXQgdGhlIGF1dG8gZ2VuZXJhdGVkIGxpbmVhciBtb2RlbApwcmludChzdW1tYXJ5KHNpbTFfYXV0b19tb2RlbCkpICMgcHJpbnQgb3V0IHRoZSBzdW1tYXJ5IG9mIHRoZSBnZW5lcmF0ZWQgbGluZWFyIG1vZGVsCgpzaW0xX2NvZWYgPSBjb2VmKHNpbTFfYXV0b19tb2RlbCkgIyByZXRyaWV2ZXMgbW9kZWwncyBpbnRlcmNlcHQgYW5kIHNsb3BlCnByaW50KHNpbTFfY29lZikKCiMgdmlzdWFsaXplIHRoZSBhdXRvIGdlbmVyYXRlZCBsaW5lYXIgbW9kZWwgb24gdG9wIG9mIHRoZSBzaW0xIGRhdGEKZ2dwbG90KHNpbTEsIGFlcyh4LCB5KSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAyLCBjb2xvdXIgPSAiZ3JleTMwIikgKyAKICBnZW9tX2FibGluZSgKICAgIGFlcyhpbnRlcmNlcHQgPSBzaW0xX2NvZWZbMV0sIHNsb3BlID0gc2ltMV9jb2VmWzJdKSAKICApCmBgYAoKTWFraW5nIFByZWRpY3Rpb24KYGBge3J9Cm5ldy5kYXRhID0gZGF0YS5mcmFtZSh4ID0gYygxLDIsMyw0LDUsNiw3LDgsOSwxMCkpCnByZWRpY3Qoc2ltMV9hdXRvX21vZGVsLCBuZXcuZGF0YSkKYGBgCgoKIyMgVXNpbmcgbG0oKSBmdW5jdGlvbiB3aXRoIGNhdGVnb3JpY2FsIGRhdGEKVXNpbmcgbG0oKSBmdW5jdGlvbiBvbiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHdpbGwgdXNlIG1lYW4gdmFsdWUgZm9yIGVhY2ggY2F0ZWdvcnkgZm9yIHByZWRpY3Rpb24uCmBgYHtyfQpwX2RhdGEobW9kZWxyKSAjIHNob3dzIGluY2x1ZGVkIGRhdGEgc2V0cyB3aXRoIG1vZGVsciBwYWNrYWdlCnNpbTIgJT4lIHN1bW1hcnkoKQpzaW0yIAptb2RlbF9jYXQgPSBzaW0yICU+JSAgbG0oeX54LCAuKQpwcmludChtb2RlbF9jYXQpCmdyaWQgPSBzaW0yICU+JSAKICBkYXRhX2dyaWQoeCkgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhtb2RlbF9jYXQpCmdyaWQKCmdncGxvdChzaW0yLCBhZXMoeCkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZ3JpZCwgYWVzKHkgPSBwcmVkKSwgY29sb3VyID0gInJlZCIsIHNpemUgPSA0KQpgYGAKCgpgYGB7cn0Kc2ltMwpgYGAKCiMjIE11bHRpcGxlIFJlZ3Jlc3Npb24KTXVsdGlwbGUgcmVncmVzc2lvbiBpcyBhbiBleHRlbnNpb24gb2YgbGluZWFyIHJlZ3Jlc3Npb24gaW50byByZWxhdGlvbnNoaXAgYmV0d2VlbiBtb3JlIHRoYW4gdHdvIHZhcmlhYmxlcy4KYGBge3J9Cj9tdGNhcnMKcHJpbnQobXRjYXJzKQptLnIubW9kZWwgPSBtdGNhcnMgJT4lIGxtKG1wZ35kaXNwK2hwK3d0LCAuKQpwcmludChtLnIubW9kZWwpCgpuZXcuZGF0YS4yID0gZGF0YS5mcmFtZSgKICBkaXNwID0gYygyMjEpLAogIGhwID0gYygxMDIpLAogIHd0ID0gYygyLjkxKQogICkgJT4lIHByaW50KCkKCnByZWRpY3QobS5yLm1vZGVsLCBuZXcuZGF0YS4yKQpgYGAKCiMjIExvZ2lzdGljIFJlZ3Jlc3Npb24KCmBgYHtyfQpwcmludChtdGNhcnMpCmJpLm1vZGVsID0gbXRjYXJzICU+JSBnbG0oYW1+Y3lsK2hwK3d0LCAuLCBmYW1pbHk9Ymlub21pYWwpCnByaW50KGJpLm1vZGVsKQoKbmV3LmRhdGEuMyA9IGRhdGEuZnJhbWUoCiAgY3lsID0gYyg0KSwKICBocCA9IGMoMTAyKSwKICB3dCA9IGMoMy45MSkKICApICU+JSBwcmludCgpCgpuZXcuZGF0YS4zYiA9IGRhdGEuZnJhbWUoCiAgY3lsID0gYyg0LDYsOCksCiAgaHAgPSBjKDEwMiwxMTAsOTgpLAogIHd0ID0gYygyLjkxLCAyLjYyLCAzLjQ0KQogICkgJT4lIHByaW50KCkKc3VtbWFyeShiaS5tb2RlbCkKcHJlZGljdChiaS5tb2RlbCwgbmV3LmRhdGEuMykKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpgYGB7cn0Kc2ltMWEgPC0gdGliYmxlKAogIHggPSByZXAoMToxMCwgZWFjaCA9IDMpLAogIHkgPSB4ICogMS41ICsgNiArIHJ0KGxlbmd0aCh4KSwgZGYgPSAyKQopCgpzaW0yCnNpbTIgJT4lIAogIHNlbGVjdCh4KSAlPiUgCiAgdGFibGUoKQpnZ3Bsb3Qoc2ltMikgKwogIGdlb21fcG9pbnQoYWVzKHgseSkpCgptb2QyIDwtIGxtKHkgfiB4LCBkYXRhID0gc2ltMikKCmdyaWQgPC0gc2ltMiAlPiUgCiAgZGF0YV9ncmlkKHgpICU+JSAKICBhZGRfcHJlZGljdGlvbnMobW9kMikKZ3JpZAoKZ2dwbG90KHNpbTIsIGFlcyh4KSkgKyAKICBnZW9tX3BvaW50KGFlcyh5ID0geSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBncmlkLCBhZXMoeSA9IHByZWQpLCBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDQpCgpzaW01IDwtIHRpYmJsZSgKICB4ID0gc2VxKDAsIDMuNSAqIHBpLCBsZW5ndGggPSA1MCksCiAgeSA9IDQgKiBzaW4oeCkgKyBybm9ybShsZW5ndGgoeCkpCikKCmdncGxvdChzaW01LCBhZXMoeCwgeSkpICsKICBnZW9tX3BvaW50KCkKbGlicmFyeShzcGxpbmVzKQptb2QxIDwtIGxtKHkgfiBucyh4LCAxKSwgZGF0YSA9IHNpbTUpCm1vZDIgPC0gbG0oeSB+IG5zKHgsIDIpLCBkYXRhID0gc2ltNSkKbW9kMyA8LSBsbSh5IH4gbnMoeCwgMyksIGRhdGEgPSBzaW01KQptb2Q0IDwtIGxtKHkgfiBucyh4LCA0KSwgZGF0YSA9IHNpbTUpCm1vZDUgPC0gbG0oeSB+IG5zKHgsIDUpLCBkYXRhID0gc2ltNSkKCmdyaWQgPC0gc2ltNSAlPiUgCiAgZGF0YV9ncmlkKHggPSBzZXFfcmFuZ2UoeCwgbiA9IDUwLCBleHBhbmQgPSAwLjEpKSAlPiUgCiAgZ2F0aGVyX3ByZWRpY3Rpb25zKG1vZDEsIG1vZDIsIG1vZDMsIG1vZDQsIG1vZDUsIC5wcmVkID0gInkiKQoKZ2dwbG90KHNpbTUsIGFlcyh4LCB5KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZShkYXRhID0gZ3JpZCwgY29sb3VyID0gInJlZCIpICsKICBmYWNldF93cmFwKH4gbW9kZWwpCmBgYAojIFZJU1VBTElaQVRJT04gVlMuIE1PREVMClZpc3VhbGl6YXRpb24gaXMgdXN1YWxseSB0aG91Z2h0IGFzIGEgdG9vbCBmb3IgaHlwb3RoZXNpcyBnZW5lcmF0aW9uIHRvIGV4cGxvcmUgdGhlIGhpZGRlbiBwYXR0ZXJucyBhbW9uZyBkYXRhIHdoaWxlIG1vZGVsbGluZyBpcyB1c3VhbGx5IHRob3VnaHQgYXMgYXQgdG9vbCBmb3IgaHlwb3RoZXNpcyBjb25maXJtYXRpb24gKHRvIGNvbmZpcm0gd2hhdCBhcmUgZm91bmQgYnkgZGF0YSB2aXN1YWxpemF0aW9uIHRvb2xzKS4gIFRoZXNlIHR3byB0b29scyBhcmUgc3VnZ2VzdGVkIHRvIHVzZWQgaW4gYW4gaXRlcmF0aXZlIG1hbm5lciBzbyBhcyB0byBhY2hpZXZlIGEgZGVlcGVyIHJldmVhbGluZyBvbiBkYXRhLgoKCiMgRlVSVEhFUiBMRUFSTklORyBSRVNPVVJDRVMKIyMgTW9yZSBvbiBnZ3Bsb3QyCgojIyMgZ2dwbG90MiB0dXRvcmlhbApgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL3d3dy50dXRvcmlhbHNwb2ludC5jb20vZ2dwbG90Mi9nZ3Bsb3QyX2ludHJvZHVjdGlvbi5odG0iKQpgYGAKCmdncGxvdDI6IEVsZWdhbnQgR3JhcGhpY3MgZm9yIERhdGEgQW5hbHlzaXMgKFdlYi1iYXNlZCBFLWJvb2spCiFbRWxlZ2FudCBHcmFwaGljcyBmb3IgRGF0YSBBbmFseXNpc10oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL2NvdmVyLmpwZykKIyMjCmBgYHtyfQpicm93c2VVUkwoImh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8iKQpgYGAKCgojIyBSIFN0YXRpc2NhbCBNb2RlbHMKCiMjIyBtb3JlIG9uIG1vZGVsIGJ1aWxkaW5nCkEgZXhlY2VsbGVudCBlYm9vayBmb3IgUiBEYXRhIFNjaWVuY2UKKiBCYXNpYyBDb25jZXB0IG9mIE1vZGVsCiogTW9kZWwgQnVpbGRpbmcKKiBFeGFtcGxlIG9uIE1vZGVsCkEgd2ViIGJhc2VkIGVib29rIGF0CiFbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2I4OGVmOTI2YTAwNGIwZmNlNzJiMjUyNmIwYjVjNDQxMzY2NmE0Y2IvMjRhMzAvY292ZXIucG5nKQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL3I0ZHMuaGFkLmNvLm56L21vZGVsLWludHJvLmh0bWwiKQpgYGAKCiMjIyBleGFtcGxlcyBvbiBtb3JlIG1vZGVscwoqIExpbmVhciBSZWdyZXNzaW9uCiogTXVsdGlwbGUgUmVncmVzc2lvbgoqIExvZ2lzdGljIFJlZ3Jlc3Npb24KKiBEZWNpc2lvbiBUcmVlCiogUmFuZG9tIEZvcmVzdAoqIEFuZCBzbyBvbgpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL3d3dy50dXRvcmlhbHNwb2ludC5jb20vci9yX2xpbmVhcl9yZWdyZXNzaW9uLmh0bSIpCmBgYAoKIyMgUiBTdHVkaW8gQ2hlYXRzaGVldHMKTWFueSBxdWljayBzeW50YXggcmVmZXJlbmNlIGZvciBSIHByb2dyYW1taW5nIGFuZCAzLXJkIHBhY2thZ2VzCiFbUiBTdHVkaW8gQ2hlYXRzaGVldHNdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL21haW4vcG5ncy9kYXRhLXZpc3VhbGl6YXRpb24ucG5nKQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvIikKYGBgCgojIyBBZHZhbmNlZCBSCiFbQWR2YW5jZWQgUl0oaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC81NjU5MTYxOThiMGJlNTFiZjg4YjM2Zjk0YjgwYzdlYTY3Y2FmZTdjLzdmNzBiL2NvdmVyLnBuZykKYGBge3J9CmJyb3dzZVVSTCgiaHR0cHM6Ly9hZHYtci5oYWRsZXkubnovaW5kZXguaHRtbCIpCmBgYAoKCgojIFRPIERFTEVURQpgYGB7cn0KcF9kYXRhKG1vZGVscikKaGVpZ2h0cwpgYGAKCg==